Merge pull request #743 from ae-utbm/taiste

Taiste
This commit is contained in:
thomas girod 2024-07-28 21:37:38 +02:00 committed by GitHub
commit 0790ae2298
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
240 changed files with 9604 additions and 8993 deletions

View File

@ -8,11 +8,7 @@ updates:
- package-ecosystem: "pip" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "daily"
# Raise pull requests for version updates
# to pip against the `develop` branch
interval: "weekly"
target-branch: "taiste"
reviewers:
- "ae-utbm/developpers-v3"
commit-message:
prefix: "[UPDATE] "

4
.gitignore vendored
View File

@ -17,4 +17,6 @@ sith/settings_custom.py
sith/search_indexes/
.coverage
coverage_report/
doc/_build
# compiled documentation
site/

View File

@ -1,10 +1,21 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.5.1
rev: v0.5.5
hooks:
- id: ruff # just check the code, and print the errors
- id: ruff # actually fix the fixable errors, but print nothing
args: ["--fix", "--silent"]
# Run the formatter.
- id: ruff-format
- id: ruff-format
- repo: https://github.com/rtts/djhtml
rev: 3.0.6
hooks:
- id: djhtml
name: format templates
entry: djhtml --tabwidth 2
types: ["jinja"]
- id: djcss
name: format scss files
entry: djcss --tabwidth 2
types: ["scss"]

View File

@ -1,27 +1,27 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}Accounting type list{% endtrans %}
{% trans %}Accounting type list{% endtrans %}
{% endblock %}
{% block content %}
<div id="accounting">
<p>
<a href="{{ url('accounting:bank_list') }}">{% trans %}Accounting{% endtrans %}</a> >
{% trans %}Accounting types{% endtrans %}
</p>
<hr>
<p><a href="{{ url('accounting:type_new') }}">{% trans %}New accounting type{% endtrans %}</a></p>
{% if accountingtype_list %}
<h3>{% trans %}Accounting type list{% endtrans %}</h3>
<ul>
{% for a in accountingtype_list %}
<li><a href="{{ url('accounting:type_edit', type_id=a.id) }}">{{ a }}</a></li>
{% endfor %}
</ul>
{% else %}
{% trans %}There is no types in this website.{% endtrans %}
{% endif %}
</div>
<div id="accounting">
<p>
<a href="{{ url('accounting:bank_list') }}">{% trans %}Accounting{% endtrans %}</a> >
{% trans %}Accounting types{% endtrans %}
</p>
<hr>
<p><a href="{{ url('accounting:type_new') }}">{% trans %}New accounting type{% endtrans %}</a></p>
{% if accountingtype_list %}
<h3>{% trans %}Accounting type list{% endtrans %}</h3>
<ul>
{% for a in accountingtype_list %}
<li><a href="{{ url('accounting:type_edit', type_id=a.id) }}">{{ a }}</a></li>
{% endfor %}
</ul>
{% else %}
{% trans %}There is no types in this website.{% endtrans %}
{% endif %}
</div>
{% endblock %}

View File

@ -1,37 +1,37 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}Bank account: {% endtrans %}{{ object.name }}
{% trans %}Bank account: {% endtrans %}{{ object.name }}
{% endblock %}
{% block content %}
<div id="accounting">
<p>
<a href="{{ url('accounting:bank_list') }}">{% trans %}Accounting{% endtrans %}</a> >
{{ object.name }}
</p>
<hr>
<h2>{% trans %}Bank account: {% endtrans %}{{ object.name }}</h2>
{% if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) and not object.club_accounts.exists() %}
<a href="{{ url('accounting:bank_delete', b_account_id=object.id) }}">{% trans %}Delete{% endtrans %}</a>
{% endif %}
<h4>{% trans %}Infos{% endtrans %}</h4>
<ul>
<li><strong>{% trans %}IBAN: {% endtrans %}</strong>{{ object.iban }}</li>
<li><strong>{% trans %}Number: {% endtrans %}</strong>{{ object.number }}</li>
</ul>
<p><a href="{{ url('accounting:club_new') }}?parent={{ object.id }}">{% trans %}New club account{% endtrans %}</a></p>
<ul>
{% for c in object.club_accounts.all() %}
<li><a href="{{ url('accounting:club_details', c_account_id=c.id) }}">{{ c }}</a>
- <a href="{{ url('accounting:club_edit', c_account_id=c.id) }}">{% trans %}Edit{% endtrans %}</a>
{% if c.journals.count() == 0 %}
- <a href="{{ url('accounting:club_delete', c_account_id=c.id) }}">{% trans %}Delete{% endtrans %}</a>
{% endif %}
</li>
{% endfor %}
</ul>
</div>
<div id="accounting">
<p>
<a href="{{ url('accounting:bank_list') }}">{% trans %}Accounting{% endtrans %}</a> >
{{ object.name }}
</p>
<hr>
<h2>{% trans %}Bank account: {% endtrans %}{{ object.name }}</h2>
{% if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) and not object.club_accounts.exists() %}
<a href="{{ url('accounting:bank_delete', b_account_id=object.id) }}">{% trans %}Delete{% endtrans %}</a>
{% endif %}
<h4>{% trans %}Infos{% endtrans %}</h4>
<ul>
<li><strong>{% trans %}IBAN: {% endtrans %}</strong>{{ object.iban }}</li>
<li><strong>{% trans %}Number: {% endtrans %}</strong>{{ object.number }}</li>
</ul>
<p><a href="{{ url('accounting:club_new') }}?parent={{ object.id }}">{% trans %}New club account{% endtrans %}</a></p>
<ul>
{% for c in object.club_accounts.all() %}
<li><a href="{{ url('accounting:club_details', c_account_id=c.id) }}">{{ c }}</a>
- <a href="{{ url('accounting:club_edit', c_account_id=c.id) }}">{% trans %}Edit{% endtrans %}</a>
{% if c.journals.count() == 0 %}
- <a href="{{ url('accounting:club_delete', c_account_id=c.id) }}">{% trans %}Delete{% endtrans %}</a>
{% endif %}
</li>
{% endfor %}
</ul>
</div>
{% endblock %}

View File

@ -1,32 +1,32 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}Bank account list{% endtrans %}
{% trans %}Bank account list{% endtrans %}
{% endblock %}
{% block content %}
<div id="accounting">
<h4>
{% trans %}Accounting{% endtrans %}
</h4>
{% if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) %}
<p><a href="{{ url('accounting:simple_type_list') }}">{% trans %}Manage simplified types{% endtrans %}</a></p>
<p><a href="{{ url('accounting:type_list') }}">{% trans %}Manage accounting types{% endtrans %}</a></p>
<p><a href="{{ url('accounting:bank_new') }}">{% trans %}New bank account{% endtrans %}</a></p>
{% endif %}
{% if bankaccount_list %}
<h3>{% trans %}Bank account list{% endtrans %}</h3>
<ul>
{% for a in object_list %}
<li><a href="{{ url('accounting:bank_details', b_account_id=a.id) }}">{{ a }}</a>
- <a href="{{ url('accounting:bank_edit', b_account_id=a.id) }}">{% trans %}Edit{% endtrans %}</a>
</li>
{% endfor %}
</ul>
{% else %}
{% trans %}There is no accounts in this website.{% endtrans %}
{% endif %}
</div>
<div id="accounting">
<h4>
{% trans %}Accounting{% endtrans %}
</h4>
{% if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) %}
<p><a href="{{ url('accounting:simple_type_list') }}">{% trans %}Manage simplified types{% endtrans %}</a></p>
<p><a href="{{ url('accounting:type_list') }}">{% trans %}Manage accounting types{% endtrans %}</a></p>
<p><a href="{{ url('accounting:bank_new') }}">{% trans %}New bank account{% endtrans %}</a></p>
{% endif %}
{% if bankaccount_list %}
<h3>{% trans %}Bank account list{% endtrans %}</h3>
<ul>
{% for a in object_list %}
<li><a href="{{ url('accounting:bank_details', b_account_id=a.id) }}">{{ a }}</a>
- <a href="{{ url('accounting:bank_edit', b_account_id=a.id) }}">{% trans %}Edit{% endtrans %}</a>
</li>
{% endfor %}
</ul>
{% else %}
{% trans %}There is no accounts in this website.{% endtrans %}
{% endif %}
</div>
{% endblock %}

View File

@ -1,68 +1,68 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}Club account:{% endtrans %} {{ object.name }}
{% trans %}Club account:{% endtrans %} {{ object.name }}
{% endblock %}
{% block content %}
<div id="accounting">
<p>
<a href="{{ url('accounting:bank_list') }}">{% trans %}Accounting{% endtrans %}</a> >
<a href="{{ url('accounting:bank_details', b_account_id=object.bank_account.id) }}">{{object.bank_account }}</a> >
{{ object }}
</p>
<hr>
<h2>{% trans %}Club account:{% endtrans %} {{ object.name }}</h2>
{% if user.is_root and not object.journals.exists() %}
<a href="{{ url('accounting:club_delete', c_account_id=object.id) }}">{% trans %}Delete{% endtrans %}</a>
{% endif %}
{% if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) %}
<p><a href="{{ url('accounting:label_new') }}?parent={{ object.id }}">{% trans %}New label{% endtrans %}</a></p>
{% endif %}
<p><a href="{{ url('accounting:label_list', clubaccount_id=object.id) }}">{% trans %}Label list{% endtrans %}</a></p>
{% if not object.has_open_journal() %}
<p><a href="{{ url('accounting:journal_new') }}?parent={{ object.id }}">{% trans %}New journal{% endtrans %}</a></p>
{% else %}
<p>{% trans %}You can not create new journal while you still have one opened{% endtrans %}</p>
{% endif %}
<table>
<thead>
<tr>
<td>{% trans %}Name{% endtrans %}</td>
<td>{% trans %}Start{% endtrans %}</td>
<td>{% trans %}End{% endtrans %}</td>
<td>{% trans %}Amount{% endtrans %}</td>
<td>{% trans %}Effective amount{% endtrans %}</td>
<td>{% trans %}Closed{% endtrans %}</td>
<td>{% trans %}Actions{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for j in object.journals.all() %}
<tr>
<td>{{ j.name }}</td>
<td>{{ j.start_date }}</td>
{% if j.end_date %}
<td>{{ j.end_date }}</td>
{% else %}
<td> - </td>
{% endif %}
<td>{{ j.amount }} €</td>
<td>{{ j.effective_amount }} €</td>
{% if j.closed %}
<td>{% trans %}Yes{% endtrans %}</td>
{% else %}
<td>{% trans %}No{% endtrans %}</td>
{% endif %}
<td> <a href="{{ url('accounting:journal_details', j_id=j.id) }}">{% trans %}View{% endtrans %}</a>
<a href="{{ url('accounting:journal_edit', j_id=j.id) }}">{% trans %}Edit{% endtrans %}</a>
{% if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) and j.operations.count() == 0 %}
<a href="{{ url('accounting:journal_delete', j_id=j.id) }}">{% trans %}Delete{% endtrans %}</a>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div id="accounting">
<p>
<a href="{{ url('accounting:bank_list') }}">{% trans %}Accounting{% endtrans %}</a> >
<a href="{{ url('accounting:bank_details', b_account_id=object.bank_account.id) }}">{{object.bank_account }}</a> >
{{ object }}
</p>
<hr>
<h2>{% trans %}Club account:{% endtrans %} {{ object.name }}</h2>
{% if user.is_root and not object.journals.exists() %}
<a href="{{ url('accounting:club_delete', c_account_id=object.id) }}">{% trans %}Delete{% endtrans %}</a>
{% endif %}
{% if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) %}
<p><a href="{{ url('accounting:label_new') }}?parent={{ object.id }}">{% trans %}New label{% endtrans %}</a></p>
{% endif %}
<p><a href="{{ url('accounting:label_list', clubaccount_id=object.id) }}">{% trans %}Label list{% endtrans %}</a></p>
{% if not object.has_open_journal() %}
<p><a href="{{ url('accounting:journal_new') }}?parent={{ object.id }}">{% trans %}New journal{% endtrans %}</a></p>
{% else %}
<p>{% trans %}You can not create new journal while you still have one opened{% endtrans %}</p>
{% endif %}
<table>
<thead>
<tr>
<td>{% trans %}Name{% endtrans %}</td>
<td>{% trans %}Start{% endtrans %}</td>
<td>{% trans %}End{% endtrans %}</td>
<td>{% trans %}Amount{% endtrans %}</td>
<td>{% trans %}Effective amount{% endtrans %}</td>
<td>{% trans %}Closed{% endtrans %}</td>
<td>{% trans %}Actions{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for j in object.journals.all() %}
<tr>
<td>{{ j.name }}</td>
<td>{{ j.start_date }}</td>
{% if j.end_date %}
<td>{{ j.end_date }}</td>
{% else %}
<td> - </td>
{% endif %}
<td>{{ j.amount }} €</td>
<td>{{ j.effective_amount }} €</td>
{% if j.closed %}
<td>{% trans %}Yes{% endtrans %}</td>
{% else %}
<td>{% trans %}No{% endtrans %}</td>
{% endif %}
<td> <a href="{{ url('accounting:journal_details', j_id=j.id) }}">{% trans %}View{% endtrans %}</a>
<a href="{{ url('accounting:journal_edit', j_id=j.id) }}">{% trans %}Edit{% endtrans %}</a>
{% if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) and j.operations.count() == 0 %}
<a href="{{ url('accounting:journal_delete', j_id=j.id) }}">{% trans %}Delete{% endtrans %}</a>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}

View File

@ -1,30 +1,30 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}Company list{% endtrans %}
{% trans %}Company list{% endtrans %}
{% endblock %}
{% block content %}
<div id="accounting">
{% if user.is_root
or user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID)
%}
<p><a href="{{ url('accounting:co_new') }}">{% trans %}Create new company{% endtrans %}</a></p>
{% endif %}
<br/>
<table>
<thead>
<tr>
<td>{% trans %}Companies{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for o in object_list %}
<tr>
<td><a href="{{ url('accounting:co_edit', co_id=o.id) }}">{{ o.get_display_name() }}</a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div id="accounting">
{% if user.is_root
or user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID)
%}
<p><a href="{{ url('accounting:co_new') }}">{% trans %}Create new company{% endtrans %}</a></p>
{% endif %}
<br/>
<table>
<thead>
<tr>
<td>{% trans %}Companies{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for o in object_list %}
<tr>
<td><a href="{{ url('accounting:co_edit', co_id=o.id) }}">{{ o.get_display_name() }}</a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}

View File

@ -1,103 +1,103 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}General journal:{% endtrans %} {{ object.name }}
{% trans %}General journal:{% endtrans %} {{ object.name }}
{% endblock %}
{% block content %}
<div id="accounting">
<p>
<a href="{{ url('accounting:bank_list') }}">{% trans %}Accounting{% endtrans %}</a> >
<a href="{{ url('accounting:bank_details', b_account_id=object.club_account.bank_account.id) }}">{{object.club_account.bank_account }}</a> >
<a href="{{ url('accounting:club_details', c_account_id=object.club_account.id) }}">{{ object.club_account }}</a> >
{{ object.name }}
</p>
<hr>
<h2>{% trans %}General journal:{% endtrans %} {{ object.name }}</h2>
<p><a href="{{ url('accounting:label_new') }}?parent={{ object.club_account.id }}">{% trans %}New label{% endtrans %}</a></p>
<p><a href="{{ url('accounting:label_list', clubaccount_id=object.club_account.id) }}">{% trans %}Label list{% endtrans %}</a></p>
<p><a href="{{ url('accounting:co_list') }}">{% trans %}Company list{% endtrans %}</a></p>
<p><strong>{% trans %}Amount: {% endtrans %}</strong>{{ object.amount }} € -
<strong>{% trans %}Effective amount: {% endtrans %}</strong>{{ object.effective_amount }} €</p>
{% if object.closed %}
<p>{% trans %}Journal is closed, you can not create operation{% endtrans %}</p>
{% else %}
<p><a href="{{ url('accounting:op_new', j_id=object.id) }}">{% trans %}New operation{% endtrans %}</a></p>
</br>
{% endif %}
<div class="journal-table">
<table>
<thead>
<div id="accounting">
<p>
<a href="{{ url('accounting:bank_list') }}">{% trans %}Accounting{% endtrans %}</a> >
<a href="{{ url('accounting:bank_details', b_account_id=object.club_account.bank_account.id) }}">{{object.club_account.bank_account }}</a> >
<a href="{{ url('accounting:club_details', c_account_id=object.club_account.id) }}">{{ object.club_account }}</a> >
{{ object.name }}
</p>
<hr>
<h2>{% trans %}General journal:{% endtrans %} {{ object.name }}</h2>
<p><a href="{{ url('accounting:label_new') }}?parent={{ object.club_account.id }}">{% trans %}New label{% endtrans %}</a></p>
<p><a href="{{ url('accounting:label_list', clubaccount_id=object.club_account.id) }}">{% trans %}Label list{% endtrans %}</a></p>
<p><a href="{{ url('accounting:co_list') }}">{% trans %}Company list{% endtrans %}</a></p>
<p><strong>{% trans %}Amount: {% endtrans %}</strong>{{ object.amount }} € -
<strong>{% trans %}Effective amount: {% endtrans %}</strong>{{ object.effective_amount }} €</p>
{% if object.closed %}
<p>{% trans %}Journal is closed, you can not create operation{% endtrans %}</p>
{% else %}
<p><a href="{{ url('accounting:op_new', j_id=object.id) }}">{% trans %}New operation{% endtrans %}</a></p>
</br>
{% endif %}
<div class="journal-table">
<table>
<thead>
<tr>
<td>{% trans %}Nb{% endtrans %}</td>
<td>{% trans %}Date{% endtrans %}</td>
<td>{% trans %}Label{% endtrans %}</td>
<td>{% trans %}Amount{% endtrans %}</td>
<td>{% trans %}Payment mode{% endtrans %}</td>
<td>{% trans %}Target{% endtrans %}</td>
<td>{% trans %}Code{% endtrans %}</td>
<td>{% trans %}Nature{% endtrans %}</td>
<td>{% trans %}Done{% endtrans %}</td>
<td>{% trans %}Comment{% endtrans %}</td>
<td>{% trans %}File{% endtrans %}</td>
<td>{% trans %}Actions{% endtrans %}</td>
<td>{% trans %}PDF{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for o in object.operations.all() %}
<tr>
<td>{% trans %}Nb{% endtrans %}</td>
<td>{% trans %}Date{% endtrans %}</td>
<td>{% trans %}Label{% endtrans %}</td>
<td>{% trans %}Amount{% endtrans %}</td>
<td>{% trans %}Payment mode{% endtrans %}</td>
<td>{% trans %}Target{% endtrans %}</td>
<td>{% trans %}Code{% endtrans %}</td>
<td>{% trans %}Nature{% endtrans %}</td>
<td>{% trans %}Done{% endtrans %}</td>
<td>{% trans %}Comment{% endtrans %}</td>
<td>{% trans %}File{% endtrans %}</td>
<td>{% trans %}Actions{% endtrans %}</td>
<td>{% trans %}PDF{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for o in object.operations.all() %}
<tr>
<td>{{ o.number }}</td>
<td>{{ o.date }}</td>
<td>{{ o.label or "" }}</td>
{% if o.accounting_type.movement_type == "DEBIT" %}
<td class="neg-amount">&nbsp;{{ o.amount }}&nbsp;€</td>
{% else %}
<td class="pos-amount">&nbsp;{{ o.amount }}&nbsp;€</td>
{% endif %}
<td>{{ o.get_mode_display() }}</td>
{% if o.target_type == "OTHER" %}
<td>{{ o.number }}</td>
<td>{{ o.date }}</td>
<td>{{ o.label or "" }}</td>
{% if o.accounting_type.movement_type == "DEBIT" %}
<td class="neg-amount">&nbsp;{{ o.amount }}&nbsp;€</td>
{% else %}
<td class="pos-amount">&nbsp;{{ o.amount }}&nbsp;€</td>
{% endif %}
<td>{{ o.get_mode_display() }}</td>
{% if o.target_type == "OTHER" %}
<td>{{ o.target_label }}</td>
{% else %}
{% else %}
<td><a href="{{ o.target.get_absolute_url() }}">{{ o.target.get_display_name() }}</a></td>
{% endif %}
<td>{{ o.accounting_type.code }}</td>
<td>{{ o.accounting_type.label }}</td>
{% if o.done %}
{% endif %}
<td>{{ o.accounting_type.code }}</td>
<td>{{ o.accounting_type.label }}</td>
{% if o.done %}
<td>{% trans %}Yes{% endtrans %}</td>
{% else %}
{% else %}
<td>{% trans %}No{% endtrans %}</td>
{% endif %}
<td>{{ o.remark }}
{% endif %}
<td>{{ o.remark }}
{% if not o.linked_operation and o.target_type == "ACCOUNT" and not o.target.has_open_journal() %}
<p><strong>
{% trans %}Warning: this operation has no linked operation because the targeted club account has no opened journal.{% endtrans %}
</strong></p>
<p><strong>
{% trans url=o.target.get_absolute_url() %}Open a journal in <a href="{{ url }}">this club account</a>, then save this operation again to make the linked operation.{% endtrans %}
</strong></p>
<p><strong>
{% trans %}Warning: this operation has no linked operation because the targeted club account has no opened journal.{% endtrans %}
</strong></p>
<p><strong>
{% trans url=o.target.get_absolute_url() %}Open a journal in <a href="{{ url }}">this club account</a>, then save this operation again to make the linked operation.{% endtrans %}
</strong></p>
{% endif %}
</td>
{% if o.invoice %}
</td>
{% if o.invoice %}
<td><a href="{{ url('core:download', file_id=o.invoice.id) }}">{{ o.invoice.name }}</a></td>
{% else %}
{% else %}
<td>-</td>
{% endif %}
<td>
{%
if o.journal.club_account.bank_account.name not in ["AE TI", "TI"]
or user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID)
%}
{% if not o.journal.closed %}
<a href="{{ url('accounting:op_edit', op_id=o.id) }}">{% trans %}Edit{% endtrans %}</a>
{% endif %}
<td>
{%
if o.journal.club_account.bank_account.name not in ["AE TI", "TI"]
or user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID)
%}
{% if not o.journal.closed %}
<a href="{{ url('accounting:op_edit', op_id=o.id) }}">{% trans %}Edit{% endtrans %}</a>
{% endif %}
{% endif %}
</td>
<td><a href="{{ url('accounting:op_pdf', op_id=o.id) }}">{% trans %}Generate{% endtrans %}</a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endif %}
</td>
<td><a href="{{ url('accounting:op_pdf', op_id=o.id) }}">{% trans %}Generate{% endtrans %}</a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock %}

View File

@ -1,33 +1,33 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}General journal:{% endtrans %} {{ object.name }}
{% trans %}General journal:{% endtrans %} {{ object.name }}
{% endblock %}
{% block content %}
<div id="accounting">
<div id="accounting">
<h3>{% trans %}Accounting statement: {% endtrans %} {{ object.name }}</h3>
<table>
<thead>
<tr>
<td>{% trans %}Operation type{% endtrans %}</td>
<td>{% trans %}Sum{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for k,v in statement.items() %}
<tr>
<td>{{ k }}</td>
<td>{{ "%.2f" % v }}</td>
</tr>
{% endfor %}
</tbody>
<thead>
<tr>
<td>{% trans %}Operation type{% endtrans %}</td>
<td>{% trans %}Sum{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for k,v in statement.items() %}
<tr>
<td>{{ k }}</td>
<td>{{ "%.2f" % v }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<p><strong>{% trans %}Amount: {% endtrans %}</strong>{{ "%.2f" % object.amount }} €</p>
<p><strong>{% trans %}Effective amount: {% endtrans %}</strong>{{ "%.2f" %object.effective_amount }} €</p>
</div>
</div>
{% endblock %}

View File

@ -1,57 +1,57 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}General journal:{% endtrans %} {{ object.name }}
{% trans %}General journal:{% endtrans %} {{ object.name }}
{% endblock %}
{% macro display_tables(dict) %}
<div id="accounting">
<div id="accounting">
<h6>{% trans %}Credit{% endtrans %}</h6>
<table>
<thead>
<tr>
<td>{% trans %}Nature of operation{% endtrans %}</td>
<td>{% trans %}Sum{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for k,v in dict['CREDIT'].items() %}
<tr>
<td>{{ k }}</td>
<td>{{ "%.2f" % v }}</td>
</tr>
{% endfor %}
</tbody>
<thead>
<tr>
<td>{% trans %}Nature of operation{% endtrans %}</td>
<td>{% trans %}Sum{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for k,v in dict['CREDIT'].items() %}
<tr>
<td>{{ k }}</td>
<td>{{ "%.2f" % v }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% trans %}Total: {% endtrans %}{{ "%.2f" % dict['CREDIT_sum'] }}
<h6>{% trans %}Debit{% endtrans %}</h6>
<table>
<thead>
<tr>
<td>{% trans %}Nature of operation{% endtrans %}</td>
<td>{% trans %}Sum{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for k,v in dict['DEBIT'].items() %}
<tr>
<td>{{ k }}</td>
<td>{{ "%.2f" % v }}</td>
</tr>
{% endfor %}
</tbody>
<thead>
<tr>
<td>{% trans %}Nature of operation{% endtrans %}</td>
<td>{% trans %}Sum{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for k,v in dict['DEBIT'].items() %}
<tr>
<td>{{ k }}</td>
<td>{{ "%.2f" % v }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% trans %}Total: {% endtrans %}{{ "%.2f" % dict['DEBIT_sum'] }}
{% endmacro %}
{% endmacro %}
{% block content %}
<h3>{% trans %}Statement by nature: {% endtrans %} {{ object.name }}</h3>
{% block content %}
<h3>{% trans %}Statement by nature: {% endtrans %} {{ object.name }}</h3>
{% for k,v in statement.items() %}
<h4 style="background: lightblue; padding: 4px;">{{ k }} : {{ "%.2f" % (v['CREDIT_sum'] - v['DEBIT_sum']) }}</h4>
{% for k,v in statement.items() %}
<h4 style="background: lightblue; padding: 4px;">{{ k }} : {{ "%.2f" % (v['CREDIT_sum'] - v['DEBIT_sum']) }}</h4>
{{ display_tables(v) }}
<hr>
{% endfor %}
</div>
{% endfor %}
</div>
{% endblock %}

View File

@ -1,37 +1,37 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}General journal:{% endtrans %} {{ object.name }}
{% trans %}General journal:{% endtrans %} {{ object.name }}
{% endblock %}
{% block content %}
<div id="accounting">
<div id="accounting">
<h3>{% trans %}Statement by person: {% endtrans %} {{ object.name }}</h3>
<h4>{% trans %}Credit{% endtrans %}</h4>
<table>
<thead>
<tr>
<td>{% trans %}Target of the operation{% endtrans %}</td>
<td>{% trans %}Sum{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for key in credit_statement.keys() %}
<tr>
{% if key.target_type == "OTHER" %}
<td>{{ o.target_label }}</td>
{% elif key %}
<td><a href="{{ key.get_absolute_url() }}">{{ key.get_display_name() }}</a></td>
{% else %}
<td></td>
{% endif %}
<td>{{ "%.2f" % credit_statement[key] }}</td>
</tr>
{% endfor %}
</tbody>
<thead>
<tr>
<td>{% trans %}Target of the operation{% endtrans %}</td>
<td>{% trans %}Sum{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for key in credit_statement.keys() %}
<tr>
{% if key.target_type == "OTHER" %}
<td>{{ o.target_label }}</td>
{% elif key %}
<td><a href="{{ key.get_absolute_url() }}">{{ key.get_display_name() }}</a></td>
{% else %}
<td></td>
{% endif %}
<td>{{ "%.2f" % credit_statement[key] }}</td>
</tr>
{% endfor %}
</tbody>
</table>
@ -40,29 +40,29 @@
<h4>{% trans %}Debit{% endtrans %}</h4>
<table>
<thead>
<tr>
<td>{% trans %}Target of the operation{% endtrans %}</td>
<td>{% trans %}Sum{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for key in debit_statement.keys() %}
<tr>
{% if key.target_type == "OTHER" %}
<td>{{ o.target_label }}</td>
{% elif key %}
<td><a href="{{ key.get_absolute_url() }}">{{ key.get_display_name() }}</a></td>
{% else %}
<td></td>
{% endif %}
<td>{{ "%.2f" % debit_statement[key] }}</td>
</tr>
{% endfor %}
</tbody>
<thead>
<tr>
<td>{% trans %}Target of the operation{% endtrans %}</td>
<td>{% trans %}Sum{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for key in debit_statement.keys() %}
<tr>
{% if key.target_type == "OTHER" %}
<td>{{ o.target_label }}</td>
{% elif key %}
<td><a href="{{ key.get_absolute_url() }}">{{ key.get_display_name() }}</a></td>
{% else %}
<td></td>
{% endif %}
<td>{{ "%.2f" % debit_statement[key] }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<p>Total : {{ "%.2f" % total_debit }}</p>
</div>
</div>
{% endblock %}

View File

@ -1,36 +1,36 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}Label list{% endtrans %}
{% trans %}Label list{% endtrans %}
{% endblock %}
{% block content %}
<div id="accounting">
<p>
<a href="{{ url('accounting:bank_list') }}">{% trans %}Accounting{% endtrans %}</a> >
<a href="{{ url('accounting:bank_details', b_account_id=object.bank_account.id) }}">{{object.bank_account }}</a> >
<a href="{{ url('accounting:club_details', c_account_id=object.id) }}">{{ object }}</a>
</p>
<hr>
<p><a href="{{ url('accounting:club_details', c_account_id=object.id) }}">{% trans %}Back to club account{% endtrans %}</a></p>
{% if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) %}
<p><a href="{{ url('accounting:label_new') }}?parent={{ object.id }}">{% trans %}New label{% endtrans %}</a></p>
{% endif %}
{% if object.labels.all() %}
<h3>{% trans %}Label list{% endtrans %}</h3>
<ul>
{% for l in object.labels.all() %}
<li><a href="{{ url('accounting:label_edit', label_id=l.id) }}">{{ l }}</a>
<div id="accounting">
<p>
<a href="{{ url('accounting:bank_list') }}">{% trans %}Accounting{% endtrans %}</a> >
<a href="{{ url('accounting:bank_details', b_account_id=object.bank_account.id) }}">{{object.bank_account }}</a> >
<a href="{{ url('accounting:club_details', c_account_id=object.id) }}">{{ object }}</a>
</p>
<hr>
<p><a href="{{ url('accounting:club_details', c_account_id=object.id) }}">{% trans %}Back to club account{% endtrans %}</a></p>
{% if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) %}
<p><a href="{{ url('accounting:label_new') }}?parent={{ object.id }}">{% trans %}New label{% endtrans %}</a></p>
{% endif %}
{% if object.labels.all() %}
<h3>{% trans %}Label list{% endtrans %}</h3>
<ul>
{% for l in object.labels.all() %}
<li><a href="{{ url('accounting:label_edit', label_id=l.id) }}">{{ l }}</a>
{% if user.is_in_group(pk=settings.SITH_GROUP_ACCOUNTING_ADMIN_ID) %}
-
<a href="{{ url('accounting:label_delete', label_id=l.id) }}">{% trans %}Delete{% endtrans %}</a>
-
<a href="{{ url('accounting:label_delete', label_id=l.id) }}">{% trans %}Delete{% endtrans %}</a>
{% endif %}
</li>
{% endfor %}
</ul>
{% else %}
{% trans %}There is no label in this club account.{% endtrans %}
{% endif %}
</div>
</li>
{% endfor %}
</ul>
{% else %}
{% trans %}There is no label in this club account.{% endtrans %}
{% endif %}
</div>
{% endblock %}

View File

@ -1,123 +1,123 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}Edit operation{% endtrans %}
{% trans %}Edit operation{% endtrans %}
{% endblock %}
{% block content %}
<div id="accounting">
<div id="accounting">
<p>
<a href="{{ url('accounting:bank_list') }}">{% trans %}Accounting{% endtrans %}</a> >
<a href="{{ url('accounting:bank_details', b_account_id=object.club_account.bank_account.id) }}">{{object.club_account.bank_account }}</a> >
<a href="{{ url('accounting:club_details', c_account_id=object.club_account.id) }}">{{ object.club_account }}</a> >
<a href="{{ url('accounting:journal_details', j_id=object.id) }}">{{ object.name }}</a> >
{% trans %}Edit operation{% endtrans %}
<a href="{{ url('accounting:bank_list') }}">{% trans %}Accounting{% endtrans %}</a> >
<a href="{{ url('accounting:bank_details', b_account_id=object.club_account.bank_account.id) }}">{{object.club_account.bank_account }}</a> >
<a href="{{ url('accounting:club_details', c_account_id=object.club_account.id) }}">{{ object.club_account }}</a> >
<a href="{{ url('accounting:journal_details', j_id=object.id) }}">{{ object.name }}</a> >
{% trans %}Edit operation{% endtrans %}
</p>
<hr>
<h2>{% trans %}Edit operation{% endtrans %}</h2>
<form action="" method="post">
{% csrf_token %}
{{ form.non_field_errors() }}
{{ form.journal }}
{{ form.target_id }}
<p>{{ form.amount.errors }}<label for="{{ form.amount.name }}">{{ form.amount.label }}</label> {{ form.amount }}</p>
<p>{{ form.remark.errors }}<label for="{{ form.remark.name }}">{{ form.remark.label }}</label> {{ form.remark }}</p>
<br />
<strong>{% trans %}Warning: if you select <em>Account</em>, the opposite operation will be created in the target account. If you don't want that, select <em>Club</em> instead of <em>Account</em>.{% endtrans %}</strong>
<p>{{ form.target_type.errors }}<label for="{{ form.target_type.name }}">{{ form.target_type.label }}</label> {{ form.target_type }}</p>
{{ form.user }}
{{ form.club }}
{{ form.club_account }}
{{ form.company }}
{{ form.target_label }}
<span id="id_need_link_full"><label>{{ form.need_link.label }}</label> {{ form.need_link }}</span>
<p>{{ form.date.errors }}<label for="{{ form.date.name }}">{{ form.date.label }}</label> {{ form.date }}</p>
<p>{{ form.mode.errors }}<label for="{{ form.mode.name }}">{{ form.mode.label }}</label> {{ form.mode }}</p>
<p>{{ form.cheque_number.errors }}<label for="{{ form.cheque_number.name }}">{{ form.cheque_number.label }}</label> {{
{% csrf_token %}
{{ form.non_field_errors() }}
{{ form.journal }}
{{ form.target_id }}
<p>{{ form.amount.errors }}<label for="{{ form.amount.name }}">{{ form.amount.label }}</label> {{ form.amount }}</p>
<p>{{ form.remark.errors }}<label for="{{ form.remark.name }}">{{ form.remark.label }}</label> {{ form.remark }}</p>
<br />
<strong>{% trans %}Warning: if you select <em>Account</em>, the opposite operation will be created in the target account. If you don't want that, select <em>Club</em> instead of <em>Account</em>.{% endtrans %}</strong>
<p>{{ form.target_type.errors }}<label for="{{ form.target_type.name }}">{{ form.target_type.label }}</label> {{ form.target_type }}</p>
{{ form.user }}
{{ form.club }}
{{ form.club_account }}
{{ form.company }}
{{ form.target_label }}
<span id="id_need_link_full"><label>{{ form.need_link.label }}</label> {{ form.need_link }}</span>
<p>{{ form.date.errors }}<label for="{{ form.date.name }}">{{ form.date.label }}</label> {{ form.date }}</p>
<p>{{ form.mode.errors }}<label for="{{ form.mode.name }}">{{ form.mode.label }}</label> {{ form.mode }}</p>
<p>{{ form.cheque_number.errors }}<label for="{{ form.cheque_number.name }}">{{ form.cheque_number.label }}</label> {{
form.cheque_number }}</p>
<p>{{ form.invoice.errors }}<label for="{{ form.invoice.name }}">{{ form.invoice.label }}</label> {{ form.invoice }}</p>
<p>{{ form.simpleaccounting_type.errors }}<label for="{{ form.simpleaccounting_type.name }}">{{
form.simpleaccounting_type.label }}</label> {{ form.simpleaccounting_type }}</p>
<p>{{ form.accounting_type.errors }}<label for="{{ form.accounting_type.name }}">{{ form.accounting_type.label }}</label> {{
<p>{{ form.invoice.errors }}<label for="{{ form.invoice.name }}">{{ form.invoice.label }}</label> {{ form.invoice }}</p>
<p>{{ form.simpleaccounting_type.errors }}<label for="{{ form.simpleaccounting_type.name }}">{{
form.simpleaccounting_type.label }}</label> {{ form.simpleaccounting_type }}</p>
<p>{{ form.accounting_type.errors }}<label for="{{ form.accounting_type.name }}">{{ form.accounting_type.label }}</label> {{
form.accounting_type }}</p>
<p>{{ form.label.errors }}<label for="{{ form.label.name }}">{{ form.label.label }}</label> {{ form.label }}</p>
<p>{{ form.done.errors }}<label for="{{ form.done.name }}">{{ form.done.label }}</label> {{ form.done }}</p>
{% if form.instance.linked_operation %}
<p>{{ form.label.errors }}<label for="{{ form.label.name }}">{{ form.label.label }}</label> {{ form.label }}</p>
<p>{{ form.done.errors }}<label for="{{ form.done.name }}">{{ form.done.label }}</label> {{ form.done }}</p>
{% if form.instance.linked_operation %}
{% set obj = form.instance.linked_operation %}
<p><strong>{% trans %}Linked operation:{% endtrans %}</strong><br>
<a href="{{ url('accounting:bank_details', b_account_id=obj.journal.club_account.bank_account.id) }}">
{{obj.journal.club_account.bank_account }}</a> >
<a href="{{ url('accounting:club_details', c_account_id=obj.journal.club_account.id) }}">{{ obj.journal.club_account }}</a> >
<a href="{{ url('accounting:journal_details', j_id=obj.journal.id) }}">{{ obj.journal }}</a> >
{{ obj.number }}
<a href="{{ url('accounting:bank_details', b_account_id=obj.journal.club_account.bank_account.id) }}">
{{obj.journal.club_account.bank_account }}</a> >
<a href="{{ url('accounting:club_details', c_account_id=obj.journal.club_account.id) }}">{{ obj.journal.club_account }}</a> >
<a href="{{ url('accounting:journal_details', j_id=obj.journal.id) }}">{{ obj.journal }}</a> >
{{ obj.number }}
</p>
{% endif %}
<p><input type="submit" value="{% trans %}Save{% endtrans %}" /></p>
{% endif %}
<p><input type="submit" value="{% trans %}Save{% endtrans %}" /></p>
</form>
{% endblock %}
{% endblock %}
{% block script %}
{{ super() }}
<script>
{% block script %}
{{ super() }}
<script>
$( function() {
var target_type = $('#id_target_type');
var user = $('#id_user_wrapper');
var club = $('#id_club_wrapper');
var club_account = $('#id_club_account_wrapper');
var company = $('#id_company_wrapper');
var other = $('#id_target_label');
var need_link = $('#id_need_link_full');
function update_targets () {
if (target_type.val() == "USER") {
console.log(user);
user.show();
club.hide();
club_account.hide();
company.hide();
other.hide();
need_link.hide();
} else if (target_type.val() == "ACCOUNT") {
club_account.show();
need_link.show();
user.hide();
club.hide();
company.hide();
other.hide();
} else if (target_type.val() == "CLUB") {
club.show();
user.hide();
club_account.hide();
company.hide();
other.hide();
need_link.hide();
} else if (target_type.val() == "COMPANY") {
company.show();
user.hide();
club_account.hide();
club.hide();
other.hide();
need_link.hide();
} else if (target_type.val() == "OTHER") {
other.show();
user.hide();
club.hide();
club_account.hide();
company.hide();
need_link.hide();
} else {
company.hide();
user.hide();
club_account.hide();
club.hide();
other.hide();
need_link.hide();
}
}
update_targets();
target_type.change(update_targets);
} );
</script>
</div>
var target_type = $('#id_target_type');
var user = $('#id_user_wrapper');
var club = $('#id_club_wrapper');
var club_account = $('#id_club_account_wrapper');
var company = $('#id_company_wrapper');
var other = $('#id_target_label');
var need_link = $('#id_need_link_full');
function update_targets () {
if (target_type.val() == "USER") {
console.log(user);
user.show();
club.hide();
club_account.hide();
company.hide();
other.hide();
need_link.hide();
} else if (target_type.val() == "ACCOUNT") {
club_account.show();
need_link.show();
user.hide();
club.hide();
company.hide();
other.hide();
} else if (target_type.val() == "CLUB") {
club.show();
user.hide();
club_account.hide();
company.hide();
other.hide();
need_link.hide();
} else if (target_type.val() == "COMPANY") {
company.show();
user.hide();
club_account.hide();
club.hide();
other.hide();
need_link.hide();
} else if (target_type.val() == "OTHER") {
other.show();
user.hide();
club.hide();
club_account.hide();
company.hide();
need_link.hide();
} else {
company.hide();
user.hide();
club_account.hide();
club.hide();
other.hide();
need_link.hide();
}
}
update_targets();
target_type.change(update_targets);
} );
</script>
</div>
{% endblock %}

View File

@ -1,16 +1,16 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}Refound account{% endtrans %}
{% trans %}Refound account{% endtrans %}
{% endblock %}
{% block content %}
<div id="accounting">
<h3>{% trans %}Refound account{% endtrans %}</h3>
<form action="" method="post">
{% csrf_token %}
{{ form.as_p() }}
<p><input type="submit" value="{% trans %}Refound{% endtrans %}" /></p>
</form>
</div>
<div id="accounting">
<h3>{% trans %}Refound account{% endtrans %}</h3>
<form action="" method="post">
{% csrf_token %}
{{ form.as_p() }}
<p><input type="submit" value="{% trans %}Refound{% endtrans %}" /></p>
</form>
</div>
{% endblock %}

View File

@ -1,27 +1,27 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}Simplified type list{% endtrans %}
{% trans %}Simplified type list{% endtrans %}
{% endblock %}
{% block content %}
<div id="accounting">
<p>
<a href="{{ url('accounting:bank_list') }}">{% trans %}Accounting{% endtrans %}</a> >
{% trans %}Simplified types{% endtrans %}
</p>
<hr>
<p><a href="{{ url('accounting:simple_type_new') }}">{% trans %}New simplified type{% endtrans %}</a></p>
{% if simplifiedaccountingtype_list %}
<h3>{% trans %}Simplified type list{% endtrans %}</h3>
<ul>
{% for a in simplifiedaccountingtype_list %}
<li><a href="{{ url('accounting:simple_type_edit', type_id=a.id) }}">{{ a }}</a></li>
{% endfor %}
</ul>
{% else %}
{% trans %}There is no types in this website.{% endtrans %}
{% endif %}
</div>
<div id="accounting">
<p>
<a href="{{ url('accounting:bank_list') }}">{% trans %}Accounting{% endtrans %}</a> >
{% trans %}Simplified types{% endtrans %}
</p>
<hr>
<p><a href="{{ url('accounting:simple_type_new') }}">{% trans %}New simplified type{% endtrans %}</a></p>
{% if simplifiedaccountingtype_list %}
<h3>{% trans %}Simplified type list{% endtrans %}</h3>
<ul>
{% for a in simplifiedaccountingtype_list %}
<li><a href="{{ url('accounting:simple_type_edit', type_id=a.id) }}">{{ a }}</a></li>
{% endfor %}
</ul>
{% else %}
{% trans %}There is no types in this website.{% endtrans %}
{% endif %}
</div>
{% endblock %}

View File

@ -264,33 +264,26 @@ class TestOperation(TestCase):
)
self.assertContains(response, "Total : 5575.72", status_code=200)
self.assertContains(response, "Total : 71.42")
self.assertContains(
response,
"""
<td><a href="/user/1/">S&#39; Kia</a></td>
<td>3.00</td>""",
content = response.content.decode()
self.assertInHTML(
"""<td><a href="/user/1/">S&#39; Kia</a></td><td>3.00</td>""", content
)
self.assertContains(
response,
"""
<td><a href="/user/1/">S&#39; Kia</a></td>
<td>823.00</td>""",
self.assertInHTML(
"""<td><a href="/user/1/">S&#39; Kia</a></td><td>823.00</td>""", content
)
def test_accounting_statement(self):
response = self.client.get(
reverse("accounting:journal_accounting_statement", args=[self.journal.id])
)
self.assertContains(
response,
assert response.status_code == 200
self.assertInHTML(
"""
<tr>
<td>443 - Crédit - Ce code n&#39;existe pas</td>
<td>3.00</td>
</tr>""",
status_code=200,
response.content.decode(),
)
self.assertContains(
response,

View File

@ -29,7 +29,7 @@ from django.utils.translation import gettext_lazy as _
from club.models import Club, Mailing, MailingSubscription, Membership
from core.models import User
from core.views.forms import SelectDate, TzAwareDateTimeField
from core.views.forms import SelectDate, SelectDateTime
from counter.models import Counter
@ -149,8 +149,12 @@ class MailingForm(forms.Form):
class SellingsForm(forms.Form):
begin_date = TzAwareDateTimeField(label=_("Begin date"), required=False)
end_date = TzAwareDateTimeField(label=_("End date"), required=False)
begin_date = forms.DateTimeField(
label=_("Begin date"), widget=SelectDateTime, required=False
)
end_date = forms.DateTimeField(
label=_("End date"), widget=SelectDateTime, required=False
)
counters = forms.ModelMultipleChoiceField(
Counter.objects.order_by("name").all(), label=_("Counter"), required=False

View File

@ -2,16 +2,16 @@
{% from 'core/macros.jinja' import user_profile_link %}
{% block content %}
<div id="club_detail">
{% if club.logo %}
<div class="club_logo"><img src="{{ club.logo.url }}" alt="{{ club.unix_name }}"></div>
{% endif %}
{% if page_revision %}
{{ page_revision|markdown }}
{% else %}
<h3>{% trans %}Club{% endtrans %}</h3>
{% endif %}
</div>
<div id="club_detail">
{% if club.logo %}
<div class="club_logo"><img src="{{ club.logo.url }}" alt="{{ club.unix_name }}"></div>
{% endif %}
{% if page_revision %}
{{ page_revision|markdown }}
{% else %}
<h3>{% trans %}Club{% endtrans %}</h3>
{% endif %}
</div>
{% endblock %}

View File

@ -1,48 +1,48 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}Club list{% endtrans %}
{% trans %}Club list{% endtrans %}
{% endblock %}
{% macro display_club(club) -%}
{% if club.is_active or user.is_root %}
<li><a href="{{ url('club:club_view', club_id=club.id) }}">{{ club.name }}</a>
{% if not club.is_active %}
({% trans %}inactive{% endtrans %})
{% endif %}
{% if club.is_active or user.is_root %}
{% if club.president %} - <a href="{{ url('core:user_profile', user_id=club.president.user.id) }}">{{ club.president.user }}</a>{% endif %}
{% if club.short_description %}<p>{{ club.short_description|markdown }}</p>{% endif %}
{% endif %}
<li><a href="{{ url('club:club_view', club_id=club.id) }}">{{ club.name }}</a>
{%- if club.children.all()|length != 0 %}
<ul>
{%- for c in club.children.order_by('name') %}
{{ display_club(c) }}
{%- endfor %}
</ul>
{%- endif -%}
</li>
{% if not club.is_active %}
({% trans %}inactive{% endtrans %})
{% endif %}
{% if club.president %} - <a href="{{ url('core:user_profile', user_id=club.president.user.id) }}">{{ club.president.user }}</a>{% endif %}
{% if club.short_description %}<p>{{ club.short_description|markdown }}</p>{% endif %}
{% endif %}
{%- if club.children.all()|length != 0 %}
<ul>
{%- for c in club.children.order_by('name') %}
{{ display_club(c) }}
{%- endfor %}
</ul>
{%- endif -%}
</li>
{%- endmacro %}
{% block content %}
{% if user.is_root %}
{% if user.is_root %}
<p><a href="{{ url('club:club_new') }}">{% trans %}New club{% endtrans %}</a></p>
{% endif %}
{% if club_list %}
{% endif %}
{% if club_list %}
<h3>{% trans %}Club list{% endtrans %}</h3>
<ul>
{%- for c in club_list.all().order_by('name') if c.parent is none %}
{%- for c in club_list.all().order_by('name') if c.parent is none %}
{{ display_club(c) }}
{%- endfor %}
{%- endfor %}
</ul>
{% else %}
{% else %}
{% trans %}There is no club in this website.{% endtrans %}
{% endif %}
{% endif %}
{% endblock %}

View File

@ -2,81 +2,81 @@
{% from 'core/macros.jinja' import user_profile_link, select_all_checkbox %}
{% block content %}
<h2>{% trans %}Club members{% endtrans %}</h2>
{% if members %}
<h2>{% trans %}Club members{% endtrans %}</h2>
{% if members %}
<form action="{{ url('club:club_members', club_id=club.id) }}" id="users_old" method="post">
{% csrf_token %}
{% set users_old = dict(form.users_old | groupby("choice_label")) %}
{% if users_old %}
{{ select_all_checkbox("users_old") }}
<p></p>
{% endif %}
<table>
<thead>
<tr>
<td>{% trans %}User{% endtrans %}</td>
<td>{% trans %}Role{% endtrans %}</td>
<td>{% trans %}Description{% endtrans %}</td>
<td>{% trans %}Since{% endtrans %}</td>
{% if users_old %}
<td>{% trans %}Mark as old{% endtrans %}</td>
{% endif %}
</tr>
</thead>
<tbody>
{% for m in members %}
<tr>
<td>{{ user_profile_link(m.user) }}</td>
<td>{{ settings.SITH_CLUB_ROLES[m.role] }}</td>
<td>{{ m.description }}</td>
<td>{{ m.start_date }}</td>
{% if users_old %}
<td>
{% set user_old = users_old[m.user.get_display_name()] %}
{% if user_old %}
{{ user_old[0].tag() }}
{% endif %}
</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>
{{ form.users_old.errors }}
{% if users_old %}
<p></p>
<input type="submit" name="submit" value="{% trans %}Mark as old{% endtrans %}">
{% endif %}
{% csrf_token %}
{% set users_old = dict(form.users_old | groupby("choice_label")) %}
{% if users_old %}
{{ select_all_checkbox("users_old") }}
<p></p>
{% endif %}
<table>
<thead>
<tr>
<td>{% trans %}User{% endtrans %}</td>
<td>{% trans %}Role{% endtrans %}</td>
<td>{% trans %}Description{% endtrans %}</td>
<td>{% trans %}Since{% endtrans %}</td>
{% if users_old %}
<td>{% trans %}Mark as old{% endtrans %}</td>
{% endif %}
</tr>
</thead>
<tbody>
{% for m in members %}
<tr>
<td>{{ user_profile_link(m.user) }}</td>
<td>{{ settings.SITH_CLUB_ROLES[m.role] }}</td>
<td>{{ m.description }}</td>
<td>{{ m.start_date }}</td>
{% if users_old %}
<td>
{% set user_old = users_old[m.user.get_display_name()] %}
{% if user_old %}
{{ user_old[0].tag() }}
{% endif %}
</td>
{% endif %}
</tr>
{% endfor %}
</tbody>
</table>
{{ form.users_old.errors }}
{% if users_old %}
<p></p>
<input type="submit" name="submit" value="{% trans %}Mark as old{% endtrans %}">
{% endif %}
</form>
{% else %}
{% else %}
<p>{% trans %}There are no members in this club.{% endtrans %}</p>
{% endif %}
<form action="{{ url('club:club_members', club_id=club.id) }}" id="add_users" method="post">
{% csrf_token %}
{{ form.non_field_errors() }}
<p>
{{ form.users.errors }}
<label for="{{ form.users.id_for_label }}">{{ form.users.label }} :</label>
{{ form.users }}
<span class="helptext">{{ form.users.help_text }}</span>
</p>
<p>
{{ form.role.errors }}
<label for="{{ form.role.id_for_label }}">{{ form.role.label }} :</label>
{{ form.role }}
</p>
{% if form.start_date %}
<p>
{{ form.start_date.errors }}
<label for="{{ form.start_date.id_for_label }}">{{ form.start_date.label }} :</label>
{{ form.start_date }}
</p>
{% endif %}
<form action="{{ url('club:club_members', club_id=club.id) }}" id="add_users" method="post">
{% csrf_token %}
{{ form.non_field_errors() }}
<p>
{{ form.users.errors }}
<label for="{{ form.users.id_for_label }}">{{ form.users.label }} :</label>
{{ form.users }}
<span class="helptext">{{ form.users.help_text }}</span>
</p>
<p>
{{ form.role.errors }}
<label for="{{ form.role.id_for_label }}">{{ form.role.label }} :</label>
{{ form.role }}
</p>
{% if form.start_date %}
<p>
{{ form.start_date.errors }}
<label for="{{ form.start_date.id_for_label }}">{{ form.start_date.label }} :</label>
{{ form.start_date }}
</p>
{% endif %}
<p>
{{ form.description.errors }}
<label for="{{ form.description.id_for_label }}">{{ form.description.label }} :</label>
{{ form.description }}
</p>
<p><input type="submit" value="{% trans %}Add{% endtrans %}" /></p>
</form>
<p>
{{ form.description.errors }}
<label for="{{ form.description.id_for_label }}">{{ form.description.label }} :</label>
{{ form.description }}
</p>
<p><input type="submit" value="{% trans %}Add{% endtrans %}" /></p>
</form>
{% endblock %}

View File

@ -2,27 +2,27 @@
{% from 'core/macros.jinja' import user_profile_link %}
{% block content %}
<h2>{% trans %}Club old members{% endtrans %}</h2>
<table>
<thead>
<td>{% trans %}User{% endtrans %}</td>
<td>{% trans %}Role{% endtrans %}</td>
<td>{% trans %}Description{% endtrans %}</td>
<td>{% trans %}From{% endtrans %}</td>
<td>{% trans %}To{% endtrans %}</td>
</thead>
<tbody>
{% for m in club.members.exclude(end_date=None).order_by('-role', 'description', '-end_date').all() %}
<tr>
<td>{{ user_profile_link(m.user) }}</td>
<td>{{ settings.SITH_CLUB_ROLES[m.role] }}</td>
<td>{{ m.description }}</td>
<td>{{ m.start_date }}</td>
<td>{{ m.end_date }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<h2>{% trans %}Club old members{% endtrans %}</h2>
<table>
<thead>
<td>{% trans %}User{% endtrans %}</td>
<td>{% trans %}Role{% endtrans %}</td>
<td>{% trans %}Description{% endtrans %}</td>
<td>{% trans %}From{% endtrans %}</td>
<td>{% trans %}To{% endtrans %}</td>
</thead>
<tbody>
{% for m in club.members.exclude(end_date=None).order_by('-role', 'description', '-end_date').all() %}
<tr>
<td>{{ user_profile_link(m.user) }}</td>
<td>{{ settings.SITH_CLUB_ROLES[m.role] }}</td>
<td>{{ m.description }}</td>
<td>{{ m.start_date }}</td>
<td>{{ m.end_date }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

View File

@ -2,65 +2,65 @@
{% from 'core/macros.jinja' import user_profile_link, paginate %}
{% block content %}
<h3>{% trans %}Sales{% endtrans %}</h3>
<form id="form" action="?page=1" method="post">
<h3>{% trans %}Sales{% endtrans %}</h3>
<form id="form" action="?page=1" method="post">
{% csrf_token %}
{{ form }}
<p><input type="submit" value="{% trans %}Show{% endtrans %}" /></p>
<p><input type="submit" value="{% trans %}Download as cvs{% endtrans %}" formaction="{{ url('club:sellings_csv', club_id=object.id) }}"/></p>
</form>
<p>
{% trans %}Quantity: {% endtrans %}{{ total_quantity }} {% trans %}units{% endtrans %}<br/>
{% trans %}Total: {% endtrans %}{{ total }} €<br/>
{% trans %}Benefit: {% endtrans %}{{ benefit }}
</p>
<table>
</form>
<p>
{% trans %}Quantity: {% endtrans %}{{ total_quantity }} {% trans %}units{% endtrans %}<br/>
{% trans %}Total: {% endtrans %}{{ total }} €<br/>
{% trans %}Benefit: {% endtrans %}{{ benefit }}
</p>
<table>
<thead>
<tr>
<td>{% trans %}Date{% endtrans %}</td>
<td>{% trans %}Counter{% endtrans %}</td>
<td>{% trans %}Barman{% endtrans %}</td>
<td>{% trans %}Customer{% endtrans %}</td>
<td>{% trans %}Label{% endtrans %}</td>
<td>{% trans %}Quantity{% endtrans %}</td>
<td>{% trans %}Total{% endtrans %}</td>
<td>{% trans %}Payment method{% endtrans %}</td>
</tr>
<tr>
<td>{% trans %}Date{% endtrans %}</td>
<td>{% trans %}Counter{% endtrans %}</td>
<td>{% trans %}Barman{% endtrans %}</td>
<td>{% trans %}Customer{% endtrans %}</td>
<td>{% trans %}Label{% endtrans %}</td>
<td>{% trans %}Quantity{% endtrans %}</td>
<td>{% trans %}Total{% endtrans %}</td>
<td>{% trans %}Payment method{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for s in paginated_result %}
{% for s in paginated_result %}
<tr>
<td>{{ s.date|localtime|date(DATETIME_FORMAT) }} {{ s.date|localtime|time(DATETIME_FORMAT) }}</td>
<td>{{ s.counter }}</td>
{% if s.seller %}
<td>{{ s.date|localtime|date(DATETIME_FORMAT) }} {{ s.date|localtime|time(DATETIME_FORMAT) }}</td>
<td>{{ s.counter }}</td>
{% if s.seller %}
<td><a href="{{ s.seller.get_absolute_url() }}">{{ s.seller.get_display_name() }}</a></td>
{% else %}
{% else %}
<td></td>
{% endif %}
{% if s.customer %}
{% endif %}
{% if s.customer %}
<td><a href="{{ s.customer.user.get_absolute_url() }}">{{ s.customer.user.get_display_name() }}</a></td>
{% else %}
{% else %}
<td></td>
{% endif %}
<td>{{ s.label }}</td>
<td>{{ s.quantity }}</td>
<td>{{ s.quantity * s.unit_price }} €</td>
<td>{{ s.get_payment_method_display() }}</td>
{% if s.is_owned_by(user) %}
<td><a href="{{ url('counter:selling_delete', selling_id=s.id) }}">{% trans %}Delete{% endtrans %}</a></td>
{% endif %}
{% endif %}
<td>{{ s.label }}</td>
<td>{{ s.quantity }}</td>
<td>{{ s.quantity * s.unit_price }} €</td>
<td>{{ s.get_payment_method_display() }}</td>
{% if s.is_owned_by(user) %}
<td><a href="{{ url('counter:selling_delete', selling_id=s.id) }}">{% trans %}Delete{% endtrans %}</a></td>
{% endif %}
</tr>
{% endfor %}
{% endfor %}
</tbody>
</table>
<script type="text/javascript">
</table>
<script type="text/javascript">
function formPagination(link){
$("form").attr("action", link.href);
link.href = "javascript:void(0)"; // block link action
$("form").submit();
$("form").attr("action", link.href);
link.href = "javascript:void(0)"; // block link action
$("form").submit();
}
</script>
{{ paginate(paginated_result, paginator, "formPagination(this)") }}
</script>
{{ paginate(paginated_result, paginator, "formPagination(this)") }}
{% endblock %}

View File

@ -1,46 +1,46 @@
{% extends "core/base.jinja" %}
{% block content %}
<h3>{% trans %}Club tools{% endtrans %}</h3>
<div>
<h3>{% trans %}Club tools{% endtrans %}</h3>
<div>
<h4>{% trans %}Communication:{% endtrans %}</h4>
<ul>
<li> <a href="{{ url('com:news_new') }}?club={{ object.id }}">{% trans %}Create a news{% endtrans %}</a></li>
<li> <a href="{{ url('com:weekmail_article') }}?club={{ object.id }}">{% trans %}Post in the Weekmail{% endtrans %}</a></li>
{% if object.trombi %}
<li> <a href="{{ url('com:news_new') }}?club={{ object.id }}">{% trans %}Create a news{% endtrans %}</a></li>
<li> <a href="{{ url('com:weekmail_article') }}?club={{ object.id }}">{% trans %}Post in the Weekmail{% endtrans %}</a></li>
{% if object.trombi %}
<li> <a href="{{ url('trombi:detail', trombi_id=object.trombi.id) }}">{% trans %}Edit Trombi{% endtrans %}</a></li>
{% else %}
{% else %}
<li> <a href="{{ url('trombi:create', club_id=object.id) }}">{% trans %}New Trombi{% endtrans %}</a></li>
<li> <a href="{{ url('club:poster_list', club_id=object.id) }}">{% trans %}Posters{% endtrans %}</a></li>
{% endif %}
{% endif %}
</ul>
<h4>{% trans %}Counters:{% endtrans %}</h4>
<ul>
{% if object.unix_name == settings.SITH_LAUNDERETTE_MANAGER['unix_name'] %}
{% for l in Launderette.objects.all() %}
<li><a href="{{ url('launderette:main_click', launderette_id=l.id) }}">{{ l }}</a></li>
{% endfor %}
{% elif object.counters.filter(type="OFFICE")|count > 0 %}
{% if object.unix_name == settings.SITH_LAUNDERETTE_MANAGER['unix_name'] %}
{% for l in Launderette.objects.all() %}
<li><a href="{{ url('launderette:main_click', launderette_id=l.id) }}">{{ l }}</a></li>
{% endfor %}
{% elif object.counters.filter(type="OFFICE")|count > 0 %}
{% for c in object.counters.filter(type="OFFICE") %}
<li>{{ c }}:
<li>{{ c }}:
<a href="{{ url('counter:details', counter_id=c.id) }}">View</a>
<a href="{{ url('counter:admin', counter_id=c.id) }}">Edit</a>
</li>
</li>
{% endfor %}
{% endif %}
{% endif %}
</ul>
{% if object.club_account.exists() %}
<h4>{% trans %}Accounting: {% endtrans %}</h4>
<ul>
<h4>{% trans %}Accounting: {% endtrans %}</h4>
<ul>
{% for ca in object.club_account.all() %}
<li><a href="{{ url('accounting:club_details', c_account_id=ca.id) }}">{{ ca.get_display_name() }}</a></li>
<li><a href="{{ url('accounting:club_details', c_account_id=ca.id) }}">{{ ca.get_display_name() }}</a></li>
{% endfor %}
</ul>
</ul>
{% endif %}
{% if object.unix_name == settings.SITH_LAUNDERETTE_MANAGER['unix_name'] %}
<li><a href="{{ url('launderette:launderette_list') }}">{% trans %}Manage launderettes{% endtrans %}</a></li>
<li><a href="{{ url('launderette:launderette_list') }}">{% trans %}Manage launderettes{% endtrans %}</a></li>
{% endif %}
</div>
</div>
{% endblock %}

View File

@ -2,107 +2,107 @@
{% from 'core/macros.jinja' import select_all_checkbox %}
{% block title %}
{% trans %}Mailing lists{% endtrans %}
{% trans %}Mailing lists{% endtrans %}
{% endblock %}
{% block content %}
<b>{% trans %}Remember : mailing lists need to be moderated, if your new created list is not shown wait until moderation takes action{% endtrans %}</b>
<b>{% trans %}Remember : mailing lists need to be moderated, if your new created list is not shown wait until moderation takes action{% endtrans %}</b>
{% if mailings_not_moderated %}
<p>{% trans %}Mailing lists waiting for moderation{% endtrans %}</p>
<ul>
{% for mailing in mailings_not_moderated %}
<li>{{ mailing.email_full }}<a href="{{ url('club:mailing_delete', mailing_id=mailing.id) }}"> - {% trans %}Delete{% endtrans %}</a></li>
{% endfor %}
</ul>
{% endif %}
{% if mailings_not_moderated %}
<p>{% trans %}Mailing lists waiting for moderation{% endtrans %}</p>
<ul>
{% for mailing in mailings_not_moderated %}
<li>{{ mailing.email_full }}<a href="{{ url('club:mailing_delete', mailing_id=mailing.id) }}"> - {% trans %}Delete{% endtrans %}</a></li>
{% endfor %}
</ul>
{% endif %}
{% if mailings_moderated %}
{% if mailings_moderated %}
{% for mailing in mailings_moderated %}
<h2>{% trans %}Mailing{% endtrans %} {{ mailing.email_full }}
<h2>{% trans %}Mailing{% endtrans %} {{ mailing.email_full }}
{%- if user.is_owner(mailing) -%}
<a href="{{ url('club:mailing_delete', mailing_id=mailing.id) }}"> - {% trans %}Delete{% endtrans %}</a>
<a href="{{ url('club:mailing_delete', mailing_id=mailing.id) }}"> - {% trans %}Delete{% endtrans %}</a>
{%- endif -%}
</h2>
<form method="GET" action="{{ url('club:mailing_generate', mailing_id=mailing.id) }}" style="display:inline-block;">
</h2>
<form method="GET" action="{{ url('club:mailing_generate', mailing_id=mailing.id) }}" style="display:inline-block;">
<input type="submit" name="generateMalingList" value="{% trans %}Generate mailing list{% endtrans %}">
</form>
{% set form_mailing_removal = form["removal_" + mailing.id|string] %}
{% if form_mailing_removal.field.choices %}
{% set ms = dict(mailing.subscriptions.all() | groupby('id')) %}
<form action="{{ url('club:mailing', club_id=club.id) }}" id="{{ form_mailing_removal.auto_id }}" method="post" enctype="multipart/form-data">
<p style="margin-bottom: 1em;">{{ select_all_checkbox(form_mailing_removal.auto_id) }}</p>
{% csrf_token %}
<input hidden type="number" name="{{ form.action.name }}" value="{{ form_actions.REMOVE_SUBSCRIPTION }}" />
<table>
</form>
{% set form_mailing_removal = form["removal_" + mailing.id|string] %}
{% if form_mailing_removal.field.choices %}
{% set ms = dict(mailing.subscriptions.all() | groupby('id')) %}
<form action="{{ url('club:mailing', club_id=club.id) }}" id="{{ form_mailing_removal.auto_id }}" method="post" enctype="multipart/form-data">
<p style="margin-bottom: 1em;">{{ select_all_checkbox(form_mailing_removal.auto_id) }}</p>
{% csrf_token %}
<input hidden type="number" name="{{ form.action.name }}" value="{{ form_actions.REMOVE_SUBSCRIPTION }}" />
<table>
<thead>
<tr>
<td>{% trans %}User{% endtrans %}</td>
<td>{% trans %}Email{% endtrans %}</td>
<td>{% trans %}Delete{% endtrans %}</td>
</tr>
<tr>
<td>{% trans %}User{% endtrans %}</td>
<td>{% trans %}Email{% endtrans %}</td>
<td>{% trans %}Delete{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for widget in form_mailing_removal.subwidgets %}
{% for widget in form_mailing_removal.subwidgets %}
{% set user = ms[widget.data.value.value][0] %}
<tr>
<td>{{ user.get_username }}</td>
<td>{{ user.get_email }}</td>
<td>{{ widget.tag() }}</td>
<td>{{ user.get_username }}</td>
<td>{{ user.get_email }}</td>
<td>{{ widget.tag() }}</td>
</tr>
{% endfor %}
{% endfor %}
</tbody>
</table>
{{ form_mailing_removal.errors }}
<p><input type="submit" value="{% trans %}Remove from mailing list{% endtrans %}" /></p>
</form>
</table>
{{ form_mailing_removal.errors }}
<p><input type="submit" value="{% trans %}Remove from mailing list{% endtrans %}" /></p>
</form>
{% else %}
{% else %}
<p><b>{% trans %}There is no subscriber for this mailing list{% endtrans %}</b></p>
{% endif %}
{% endif %}
{% endfor %}
{% else %}
{% else %}
<p>{% trans %}No mailing list existing for this club{% endtrans %}</p>
{% endif %}
{% endif %}
<p>{{ form.non_field_errors() }}</p>
{% if mailings_moderated %}
<h2>{% trans %}New member{% endtrans %}</h2>
<form action="{{ url('club:mailing', club_id=club.id) }}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>
{{ form.subscription_mailing.errors }}
<label for="{{ form.subscription_mailing.id_for_label }}">{{ form.subscription_mailing.label }}</label>
{{ form.subscription_mailing }}
</p>
<p>
{{ form.subscription_users.errors }}
<label for="{{ form.subscription_users.id_for_label }}">{{ form.subscription_users.label }}</label>
{{ form.subscription_users }}
<span class="helptext">{{ form.subscription_users.help_text }}</span>
</p>
<p>
{{ form.subscription_email.errors }}
<label for="{{ form.subscription_email.id_for_label }}">{{ form.subscription_email.label }}</label>
{{ form.subscription_email }}
</p>
<input hidden type="number" name="{{ form.action.name }}" value="{{ form_actions.NEW_SUBSCRIPTION }}" />
<p><input type="submit" value="{% trans %}Add to mailing list{% endtrans %}" /></p>
</form>
{% endif %}
<h2>{% trans %}New mailing{% endtrans %}</h2>
<p>{{ form.non_field_errors() }}</p>
{% if mailings_moderated %}
<h2>{% trans %}New member{% endtrans %}</h2>
<form action="{{ url('club:mailing', club_id=club.id) }}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>
{{ form.mailing_email.errors }}
<label for="{{ form.mailing_email.id_for_label }}">{{ form.mailing_email.label }}</label>
{{ form.mailing_email }}
</p>
<input hidden type="number" name="{{ form.action.name }}" value="{{ form_actions.NEW_MALING }}" />
<p><input type="submit" value="{% trans %}Create mailing list{% endtrans %}" /></p>
{% csrf_token %}
<p>
{{ form.subscription_mailing.errors }}
<label for="{{ form.subscription_mailing.id_for_label }}">{{ form.subscription_mailing.label }}</label>
{{ form.subscription_mailing }}
</p>
<p>
{{ form.subscription_users.errors }}
<label for="{{ form.subscription_users.id_for_label }}">{{ form.subscription_users.label }}</label>
{{ form.subscription_users }}
<span class="helptext">{{ form.subscription_users.help_text }}</span>
</p>
<p>
{{ form.subscription_email.errors }}
<label for="{{ form.subscription_email.id_for_label }}">{{ form.subscription_email.label }}</label>
{{ form.subscription_email }}
</p>
<input hidden type="number" name="{{ form.action.name }}" value="{{ form_actions.NEW_SUBSCRIPTION }}" />
<p><input type="submit" value="{% trans %}Add to mailing list{% endtrans %}" /></p>
</form>
{% endif %}
<h2>{% trans %}New mailing{% endtrans %}</h2>
<form action="{{ url('club:mailing', club_id=club.id) }}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>
{{ form.mailing_email.errors }}
<label for="{{ form.mailing_email.id_for_label }}">{{ form.mailing_email.label }}</label>
{{ form.mailing_email }}
</p>
<input hidden type="number" name="{{ form.action.name }}" value="{{ form_actions.NEW_MALING }}" />
<p><input type="submit" value="{% trans %}Create mailing list{% endtrans %}" /></p>
</form>
{% endblock %}

View File

@ -2,11 +2,11 @@
{% from 'core/macros_pages.jinja' import page_history %}
{% block content %}
{% if club.page %}
{{ page_history(club.page) }}
{% else %}
{% trans %}No page existing for this club{% endtrans %}
{% endif %}
{% if club.page %}
{{ page_history(club.page) }}
{% else %}
{% trans %}No page existing for this club{% endtrans %}
{% endif %}
{% endblock %}

View File

@ -2,7 +2,7 @@
{% from 'core/macros_pages.jinja' import page_edit_form %}
{% block content %}
{{ page_edit_form(page, form, url('club:club_edit_page', club_id=page.club.id), csrf_token) }}
{{ page_edit_form(page, form, url('club:club_edit_page', club_id=page.club.id), csrf_token) }}
{% endblock %}

View File

@ -1,48 +1,48 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}Club stats{% endtrans %}
{% trans %}Club stats{% endtrans %}
{% endblock %}
{% block content %}
{% if club_list %}
{% if club_list %}
<h3>{% trans %}Club stats{% endtrans %}</h3>
<form action="" method="GET">
{% csrf_token %}
<p>
{% csrf_token %}
<p>
<select name="branch">
{% for b in settings.SITH_PROFILE_DEPARTMENTS %}
<option value="{{ b[0] }}">{{ b[0] }}</option>
{% endfor %}
{% for b in settings.SITH_PROFILE_DEPARTMENTS %}
<option value="{{ b[0] }}">{{ b[0] }}</option>
{% endfor %}
</select>
</p>
<p><input type="submit" value="{% trans %}Show{% endtrans %}" /></p>
</p>
<p><input type="submit" value="{% trans %}Show{% endtrans %}" /></p>
</form>
<table>
<thead>
<tr>
<td>Club</td>
<td>Member number</td>
<td>Old member number</td>
</tr>
</thead>
<tbody>
<thead>
<tr>
<td>Club</td>
<td>Member number</td>
<td>Old member number</td>
</tr>
</thead>
<tbody>
{% for c in club_list.order_by('id') %}
{% set members = c.members.all() %}
{% if request.GET['branch'] %}
{% set members = c.members.all() %}
{% if request.GET['branch'] %}
{% set members = members.filter(user__department=request.GET['branch']) %}
{% endif %}
<tr>
<td>{{ c.get_display_name() }}</td>
<td>{{ members.filter(end_date=None, role__gt=settings.SITH_MAXIMUM_FREE_ROLE).count() }}</td>
<td>{{ members.exclude(end_date=None, role__gt=settings.SITH_MAXIMUM_FREE_ROLE).count() }}</td>
</tr>
{% endif %}
<tr>
<td>{{ c.get_display_name() }}</td>
<td>{{ members.filter(end_date=None, role__gt=settings.SITH_MAXIMUM_FREE_ROLE).count() }}</td>
<td>{{ members.exclude(end_date=None, role__gt=settings.SITH_MAXIMUM_FREE_ROLE).count() }}</td>
</tr>
{% endfor %}
</tbody>
</tbody>
</table>
{% else %}
{% else %}
{% trans %}There is no club in this website.{% endtrans %}
{% endif %}
{% endif %}
{% endblock %}

View File

@ -1,43 +1,43 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}Mailing lists administration{% endtrans %}
{% trans %}Mailing lists administration{% endtrans %}
{% endblock %}
{% macro display_mailings(list) %}
<table>
<tr>
<th>{% trans %}Email{% endtrans %}</th>
<th>{% trans %}Club{%endtrans%}</th>
<th>{% trans %}Actions{% endtrans %}</th>
</tr>
{% for mailing in list %}
<tr>
<td>{{ mailing.email_full }}</td>
<td><a href="{{ url('club:mailing', club_id=mailing.club.id) }}">{{ mailing.club }}</a></td>
<td>
<a href="{{ url('com:mailing_delete', mailing_id=mailing.id) }}">{% trans %}Delete{% endtrans %}</a> - {% if not mailing.is_moderated %}<a href="{{ url('com:mailing_moderate', mailing_id=mailing.id) }}">{% trans %}Moderate{% endtrans %}</a>{% else %}{% trans user=mailing.moderator %}Moderated by {{ user }}{% endtrans %}{% endif %}
</td>
</tr>
{% endfor %}
</table>
<table>
<tr>
<th>{% trans %}Email{% endtrans %}</th>
<th>{% trans %}Club{%endtrans%}</th>
<th>{% trans %}Actions{% endtrans %}</th>
</tr>
{% for mailing in list %}
<tr>
<td>{{ mailing.email_full }}</td>
<td><a href="{{ url('club:mailing', club_id=mailing.club.id) }}">{{ mailing.club }}</a></td>
<td>
<a href="{{ url('com:mailing_delete', mailing_id=mailing.id) }}">{% trans %}Delete{% endtrans %}</a> - {% if not mailing.is_moderated %}<a href="{{ url('com:mailing_moderate', mailing_id=mailing.id) }}">{% trans %}Moderate{% endtrans %}</a>{% else %}{% trans user=mailing.moderator %}Moderated by {{ user }}{% endtrans %}{% endif %}
</td>
</tr>
{% endfor %}
</table>
{% endmacro %}
{% block content %}
<h1>{% trans %}This page lists all mailing lists{% endtrans %}</h1>
<h1>{% trans %}This page lists all mailing lists{% endtrans %}</h1>
{% if has_unmoderated %}
{% if has_unmoderated %}
<h2>{% trans %}Not moderated mailing lists{% endtrans %}</h2>
{{ display_mailings(unmoderated) }}
{% endif %}
{% endif %}
<h2>{% trans %}Moderated mailing lists{% endtrans %}</h2>
{% if has_moderated %}
{{ display_mailings(moderated) }}
{% else %}
<h2>{% trans %}Moderated mailing lists{% endtrans %}</h2>
{% if has_moderated %}
{{ display_mailings(moderated) }}
{% else %}
<p>{% trans %}No mailing list existing{% endtrans %}</p>
{% endif %}
{% endif %}
{% endblock %}

View File

@ -2,317 +2,317 @@
{% from 'core/macros.jinja' import user_profile_link %}
{% block title %}
{% trans %}News admin{% endtrans %}
{% trans %}News admin{% endtrans %}
{% endblock %}
{% block content %}
<h3>{% trans %}News{% endtrans %}</h3>
<h3>{% trans %}News{% endtrans %}</h3>
<p><a href="{{ url('com:news_new') }}">{% trans %}Create news{% endtrans %}</a></p>
<p><a href="{{ url('com:news_new') }}">{% trans %}Create news{% endtrans %}</a></p>
<hr />
<h4>{% trans %}Notices{% endtrans %}</h4>
{% set notices = object_list.filter(type="NOTICE").distinct().order_by('id') %}
<h5>{% trans %}Displayed notices{% endtrans %}</h5>
<table>
<thead>
<tr>
<td>{% trans %}Type{% endtrans %}</td>
<td>{% trans %}Title{% endtrans %}</td>
<td>{% trans %}Summary{% endtrans %}</td>
<td>{% trans %}Club{% endtrans %}</td>
<td>{% trans %}Author{% endtrans %}</td>
<td>{% trans %}Moderator{% endtrans %}</td>
<td>{% trans %}Actions{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for news in notices.filter(is_moderated=True) %}
<tr>
<td>{{ news.get_type_display() }}</td>
<td>{{ news.title }}</td>
<td>{{ news.summary|markdown }}</td>
<td><a href="{{ news.club.get_absolute_url() }}">{{ news.club }}</a></td>
<td>{{ user_profile_link(news.author) }}</td>
<td>{{ user_profile_link(news.moderator) }}</td>
<td><a href="{{ url('com:news_detail', news_id=news.id) }}">{% trans %}View{% endtrans %}</a>
<a href="{{ url('com:news_edit', news_id=news.id) }}">{% trans %}Edit{% endtrans %}</a>
<a href="{{ url('com:news_moderate', news_id=news.id) }}?remove">{% trans %}Remove{% endtrans %}</a>
<a href="{{ url('com:news_delete', news_id=news.id) }}">{% trans %}Delete{% endtrans %}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<h5>{% trans %}Notices to moderate{% endtrans %}</h5>
<table>
<thead>
<tr>
<td>{% trans %}Type{% endtrans %}</td>
<td>{% trans %}Title{% endtrans %}</td>
<td>{% trans %}Summary{% endtrans %}</td>
<td>{% trans %}Club{% endtrans %}</td>
<td>{% trans %}Author{% endtrans %}</td>
<td>{% trans %}Actions{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for news in notices.filter(is_moderated=False) %}
<tr>
<td>{{ news.get_type_display() }}</td>
<td>{{ news.title }}</td>
<td>{{ news.summary|markdown }}</td>
<td><a href="{{ news.club.get_absolute_url() }}">{{ news.club }}</a></td>
<td>{{ user_profile_link(news.author) }}</td>
<td><a href="{{ url('com:news_detail', news_id=news.id) }}">{% trans %}View{% endtrans %}</a>
<a href="{{ url('com:news_edit', news_id=news.id) }}">{% trans %}Edit{% endtrans %}</a>
<a href="{{ url('com:news_moderate', news_id=news.id) }}">{% trans %}Moderate{% endtrans %}</a>
<a href="{{ url('com:news_delete', news_id=news.id) }}">{% trans %}Delete{% endtrans %}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<hr />
<h4>{% trans %}Notices{% endtrans %}</h4>
{% set notices = object_list.filter(type="NOTICE").distinct().order_by('id') %}
<h5>{% trans %}Displayed notices{% endtrans %}</h5>
<table>
<thead>
<tr>
<td>{% trans %}Type{% endtrans %}</td>
<td>{% trans %}Title{% endtrans %}</td>
<td>{% trans %}Summary{% endtrans %}</td>
<td>{% trans %}Club{% endtrans %}</td>
<td>{% trans %}Author{% endtrans %}</td>
<td>{% trans %}Moderator{% endtrans %}</td>
<td>{% trans %}Actions{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for news in notices.filter(is_moderated=True) %}
<tr>
<td>{{ news.get_type_display() }}</td>
<td>{{ news.title }}</td>
<td>{{ news.summary|markdown }}</td>
<td><a href="{{ news.club.get_absolute_url() }}">{{ news.club }}</a></td>
<td>{{ user_profile_link(news.author) }}</td>
<td>{{ user_profile_link(news.moderator) }}</td>
<td><a href="{{ url('com:news_detail', news_id=news.id) }}">{% trans %}View{% endtrans %}</a>
<a href="{{ url('com:news_edit', news_id=news.id) }}">{% trans %}Edit{% endtrans %}</a>
<a href="{{ url('com:news_moderate', news_id=news.id) }}?remove">{% trans %}Remove{% endtrans %}</a>
<a href="{{ url('com:news_delete', news_id=news.id) }}">{% trans %}Delete{% endtrans %}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<h5>{% trans %}Notices to moderate{% endtrans %}</h5>
<table>
<thead>
<tr>
<td>{% trans %}Type{% endtrans %}</td>
<td>{% trans %}Title{% endtrans %}</td>
<td>{% trans %}Summary{% endtrans %}</td>
<td>{% trans %}Club{% endtrans %}</td>
<td>{% trans %}Author{% endtrans %}</td>
<td>{% trans %}Actions{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for news in notices.filter(is_moderated=False) %}
<tr>
<td>{{ news.get_type_display() }}</td>
<td>{{ news.title }}</td>
<td>{{ news.summary|markdown }}</td>
<td><a href="{{ news.club.get_absolute_url() }}">{{ news.club }}</a></td>
<td>{{ user_profile_link(news.author) }}</td>
<td><a href="{{ url('com:news_detail', news_id=news.id) }}">{% trans %}View{% endtrans %}</a>
<a href="{{ url('com:news_edit', news_id=news.id) }}">{% trans %}Edit{% endtrans %}</a>
<a href="{{ url('com:news_moderate', news_id=news.id) }}">{% trans %}Moderate{% endtrans %}</a>
<a href="{{ url('com:news_delete', news_id=news.id) }}">{% trans %}Delete{% endtrans %}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<hr />
<h4>{% trans %}Weeklies{% endtrans %}</h4>
{% set weeklies = object_list.filter(type="WEEKLY", dates__end_date__gte=timezone.now()).distinct().order_by('id') %}
<h5>{% trans %}Displayed weeklies{% endtrans %}</h5>
<table>
<thead>
<tr>
<td>{% trans %}Type{% endtrans %}</td>
<td>{% trans %}Title{% endtrans %}</td>
<td>{% trans %}Summary{% endtrans %}</td>
<td>{% trans %}Club{% endtrans %}</td>
<td>{% trans %}Author{% endtrans %}</td>
<td>{% trans %}Moderator{% endtrans %}</td>
<td>{% trans %}Dates{% endtrans %}</td>
<td>{% trans %}Actions{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for news in weeklies.filter(is_moderated=True) %}
<tr>
<td>{{ news.get_type_display() }}</td>
<td>{{ news.title }}</td>
<td>{{ news.summary|markdown }}</td>
<td><a href="{{ news.club.get_absolute_url() }}">{{ news.club }}</a></td>
<td>{{ user_profile_link(news.author) }}</td>
<td>{{ user_profile_link(news.moderator) }}</td>
<td>
<ul>
{% for d in news.dates.all() %}
<li>{{ d.start_date|localtime|date(DATETIME_FORMAT) }}
{{ d.start_date|localtime|time(DATETIME_FORMAT) }} -
{{ d.end_date|localtime|date(DATETIME_FORMAT) }}
{{ d.end_date|localtime|time(DATETIME_FORMAT) }}
</li>
{% endfor %}
</ul>
</td>
<td><a href="{{ url('com:news_detail', news_id=news.id) }}">{% trans %}View{% endtrans %}</a>
<a href="{{ url('com:news_edit', news_id=news.id) }}">{% trans %}Edit{% endtrans %}</a>
<a href="{{ url('com:news_moderate', news_id=news.id) }}?remove">{% trans %}Remove{% endtrans %}</a>
<a href="{{ url('com:news_delete', news_id=news.id) }}">{% trans %}Delete{% endtrans %}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<h5>{% trans %}Weeklies to moderate{% endtrans %}</h5>
<table>
<thead>
<tr>
<td>{% trans %}Type{% endtrans %}</td>
<td>{% trans %}Title{% endtrans %}</td>
<td>{% trans %}Summary{% endtrans %}</td>
<td>{% trans %}Club{% endtrans %}</td>
<td>{% trans %}Author{% endtrans %}</td>
<td>{% trans %}Dates{% endtrans %}</td>
<td>{% trans %}Actions{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for news in weeklies.filter(is_moderated=False) %}
<tr>
<td>{{ news.get_type_display() }}</td>
<td>{{ news.title }}</td>
<td>{{ news.summary|markdown }}</td>
<td><a href="{{ news.club.get_absolute_url() }}">{{ news.club }}</a></td>
<td>{{ user_profile_link(news.author) }}</td>
<td>
<ul>
{% for d in news.dates.all() %}
<li>{{ d.start_date|localtime|date(DATETIME_FORMAT) }}
{{ d.start_date|localtime|time(DATETIME_FORMAT) }} -
{{ d.end_date|localtime|date(DATETIME_FORMAT) }}
{{ d.end_date|localtime|time(DATETIME_FORMAT) }}
</li>
{% endfor %}
</ul>
</td>
<td><a href="{{ url('com:news_detail', news_id=news.id) }}">{% trans %}View{% endtrans %}</a>
<a href="{{ url('com:news_edit', news_id=news.id) }}">{% trans %}Edit{% endtrans %}</a>
<a href="{{ url('com:news_moderate', news_id=news.id) }}">{% trans %}Moderate{% endtrans %}</a>
<a href="{{ url('com:news_delete', news_id=news.id) }}">{% trans %}Delete{% endtrans %}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<hr />
<h4>{% trans %}Weeklies{% endtrans %}</h4>
{% set weeklies = object_list.filter(type="WEEKLY", dates__end_date__gte=timezone.now()).distinct().order_by('id') %}
<h5>{% trans %}Displayed weeklies{% endtrans %}</h5>
<table>
<thead>
<tr>
<td>{% trans %}Type{% endtrans %}</td>
<td>{% trans %}Title{% endtrans %}</td>
<td>{% trans %}Summary{% endtrans %}</td>
<td>{% trans %}Club{% endtrans %}</td>
<td>{% trans %}Author{% endtrans %}</td>
<td>{% trans %}Moderator{% endtrans %}</td>
<td>{% trans %}Dates{% endtrans %}</td>
<td>{% trans %}Actions{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for news in weeklies.filter(is_moderated=True) %}
<tr>
<td>{{ news.get_type_display() }}</td>
<td>{{ news.title }}</td>
<td>{{ news.summary|markdown }}</td>
<td><a href="{{ news.club.get_absolute_url() }}">{{ news.club }}</a></td>
<td>{{ user_profile_link(news.author) }}</td>
<td>{{ user_profile_link(news.moderator) }}</td>
<td>
<ul>
{% for d in news.dates.all() %}
<li>{{ d.start_date|localtime|date(DATETIME_FORMAT) }}
{{ d.start_date|localtime|time(DATETIME_FORMAT) }} -
{{ d.end_date|localtime|date(DATETIME_FORMAT) }}
{{ d.end_date|localtime|time(DATETIME_FORMAT) }}
</li>
{% endfor %}
</ul>
</td>
<td><a href="{{ url('com:news_detail', news_id=news.id) }}">{% trans %}View{% endtrans %}</a>
<a href="{{ url('com:news_edit', news_id=news.id) }}">{% trans %}Edit{% endtrans %}</a>
<a href="{{ url('com:news_moderate', news_id=news.id) }}?remove">{% trans %}Remove{% endtrans %}</a>
<a href="{{ url('com:news_delete', news_id=news.id) }}">{% trans %}Delete{% endtrans %}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<h5>{% trans %}Weeklies to moderate{% endtrans %}</h5>
<table>
<thead>
<tr>
<td>{% trans %}Type{% endtrans %}</td>
<td>{% trans %}Title{% endtrans %}</td>
<td>{% trans %}Summary{% endtrans %}</td>
<td>{% trans %}Club{% endtrans %}</td>
<td>{% trans %}Author{% endtrans %}</td>
<td>{% trans %}Dates{% endtrans %}</td>
<td>{% trans %}Actions{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for news in weeklies.filter(is_moderated=False) %}
<tr>
<td>{{ news.get_type_display() }}</td>
<td>{{ news.title }}</td>
<td>{{ news.summary|markdown }}</td>
<td><a href="{{ news.club.get_absolute_url() }}">{{ news.club }}</a></td>
<td>{{ user_profile_link(news.author) }}</td>
<td>
<ul>
{% for d in news.dates.all() %}
<li>{{ d.start_date|localtime|date(DATETIME_FORMAT) }}
{{ d.start_date|localtime|time(DATETIME_FORMAT) }} -
{{ d.end_date|localtime|date(DATETIME_FORMAT) }}
{{ d.end_date|localtime|time(DATETIME_FORMAT) }}
</li>
{% endfor %}
</ul>
</td>
<td><a href="{{ url('com:news_detail', news_id=news.id) }}">{% trans %}View{% endtrans %}</a>
<a href="{{ url('com:news_edit', news_id=news.id) }}">{% trans %}Edit{% endtrans %}</a>
<a href="{{ url('com:news_moderate', news_id=news.id) }}">{% trans %}Moderate{% endtrans %}</a>
<a href="{{ url('com:news_delete', news_id=news.id) }}">{% trans %}Delete{% endtrans %}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<hr />
<h4>{% trans %}Calls{% endtrans %}</h4>
{% set calls = object_list.filter(type="CALL", dates__end_date__gte=timezone.now()).distinct().order_by('id') %}
<h5>{% trans %}Displayed calls{% endtrans %}</h5>
<table>
<thead>
<tr>
<td>{% trans %}Type{% endtrans %}</td>
<td>{% trans %}Title{% endtrans %}</td>
<td>{% trans %}Summary{% endtrans %}</td>
<td>{% trans %}Club{% endtrans %}</td>
<td>{% trans %}Author{% endtrans %}</td>
<td>{% trans %}Moderator{% endtrans %}</td>
<td>{% trans %}Start{% endtrans %}</td>
<td>{% trans %}End{% endtrans %}</td>
<td>{% trans %}Actions{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for news in calls.filter(is_moderated=True) %}
<tr>
<td>{{ news.get_type_display() }}</td>
<td>{{ news.title }}</td>
<td>{{ news.summary|markdown }}</td>
<td><a href="{{ news.club.get_absolute_url() }}">{{ news.club }}</a></td>
<td>{{ user_profile_link(news.author) }}</td>
<td>{{ user_profile_link(news.moderator) }}</td>
<td>{{ news.dates.first().start_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().start_date|localtime|time(DATETIME_FORMAT) }}</td>
<td>{{ news.dates.first().end_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().end_date|localtime|time(DATETIME_FORMAT) }}</td>
<td><a href="{{ url('com:news_detail', news_id=news.id) }}">{% trans %}View{% endtrans %}</a>
<a href="{{ url('com:news_edit', news_id=news.id) }}">{% trans %}Edit{% endtrans %}</a>
<a href="{{ url('com:news_moderate', news_id=news.id) }}?remove">{% trans %}Remove{% endtrans %}</a>
<a href="{{ url('com:news_delete', news_id=news.id) }}">{% trans %}Delete{% endtrans %}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<h5>{% trans %}Calls to moderate{% endtrans %}</h5>
<table>
<thead>
<tr>
<td>{% trans %}Type{% endtrans %}</td>
<td>{% trans %}Title{% endtrans %}</td>
<td>{% trans %}Summary{% endtrans %}</td>
<td>{% trans %}Club{% endtrans %}</td>
<td>{% trans %}Author{% endtrans %}</td>
<td>{% trans %}Start{% endtrans %}</td>
<td>{% trans %}End{% endtrans %}</td>
<td>{% trans %}Actions{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for news in calls.filter(is_moderated=False) %}
<tr>
<td>{{ news.get_type_display() }}</td>
<td>{{ news.title }}</td>
<td>{{ news.summary|markdown }}</td>
<td><a href="{{ news.club.get_absolute_url() }}">{{ news.club }}</a></td>
<td>{{ user_profile_link(news.author) }}</td>
<td>{{ news.dates.first().start_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().start_date|localtime|time(DATETIME_FORMAT) }}</td>
<td>{{ news.dates.first().end_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().end_date|localtime|time(DATETIME_FORMAT) }}</td>
<td><a href="{{ url('com:news_detail', news_id=news.id) }}">{% trans %}View{% endtrans %}</a>
<a href="{{ url('com:news_edit', news_id=news.id) }}">{% trans %}Edit{% endtrans %}</a>
<a href="{{ url('com:news_moderate', news_id=news.id) }}">{% trans %}Moderate{% endtrans %}</a>
<a href="{{ url('com:news_delete', news_id=news.id) }}">{% trans %}Delete{% endtrans %}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<hr />
<h4>{% trans %}Calls{% endtrans %}</h4>
{% set calls = object_list.filter(type="CALL", dates__end_date__gte=timezone.now()).distinct().order_by('id') %}
<h5>{% trans %}Displayed calls{% endtrans %}</h5>
<table>
<thead>
<tr>
<td>{% trans %}Type{% endtrans %}</td>
<td>{% trans %}Title{% endtrans %}</td>
<td>{% trans %}Summary{% endtrans %}</td>
<td>{% trans %}Club{% endtrans %}</td>
<td>{% trans %}Author{% endtrans %}</td>
<td>{% trans %}Moderator{% endtrans %}</td>
<td>{% trans %}Start{% endtrans %}</td>
<td>{% trans %}End{% endtrans %}</td>
<td>{% trans %}Actions{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for news in calls.filter(is_moderated=True) %}
<tr>
<td>{{ news.get_type_display() }}</td>
<td>{{ news.title }}</td>
<td>{{ news.summary|markdown }}</td>
<td><a href="{{ news.club.get_absolute_url() }}">{{ news.club }}</a></td>
<td>{{ user_profile_link(news.author) }}</td>
<td>{{ user_profile_link(news.moderator) }}</td>
<td>{{ news.dates.first().start_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().start_date|localtime|time(DATETIME_FORMAT) }}</td>
<td>{{ news.dates.first().end_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().end_date|localtime|time(DATETIME_FORMAT) }}</td>
<td><a href="{{ url('com:news_detail', news_id=news.id) }}">{% trans %}View{% endtrans %}</a>
<a href="{{ url('com:news_edit', news_id=news.id) }}">{% trans %}Edit{% endtrans %}</a>
<a href="{{ url('com:news_moderate', news_id=news.id) }}?remove">{% trans %}Remove{% endtrans %}</a>
<a href="{{ url('com:news_delete', news_id=news.id) }}">{% trans %}Delete{% endtrans %}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<h5>{% trans %}Calls to moderate{% endtrans %}</h5>
<table>
<thead>
<tr>
<td>{% trans %}Type{% endtrans %}</td>
<td>{% trans %}Title{% endtrans %}</td>
<td>{% trans %}Summary{% endtrans %}</td>
<td>{% trans %}Club{% endtrans %}</td>
<td>{% trans %}Author{% endtrans %}</td>
<td>{% trans %}Start{% endtrans %}</td>
<td>{% trans %}End{% endtrans %}</td>
<td>{% trans %}Actions{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for news in calls.filter(is_moderated=False) %}
<tr>
<td>{{ news.get_type_display() }}</td>
<td>{{ news.title }}</td>
<td>{{ news.summary|markdown }}</td>
<td><a href="{{ news.club.get_absolute_url() }}">{{ news.club }}</a></td>
<td>{{ user_profile_link(news.author) }}</td>
<td>{{ news.dates.first().start_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().start_date|localtime|time(DATETIME_FORMAT) }}</td>
<td>{{ news.dates.first().end_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().end_date|localtime|time(DATETIME_FORMAT) }}</td>
<td><a href="{{ url('com:news_detail', news_id=news.id) }}">{% trans %}View{% endtrans %}</a>
<a href="{{ url('com:news_edit', news_id=news.id) }}">{% trans %}Edit{% endtrans %}</a>
<a href="{{ url('com:news_moderate', news_id=news.id) }}">{% trans %}Moderate{% endtrans %}</a>
<a href="{{ url('com:news_delete', news_id=news.id) }}">{% trans %}Delete{% endtrans %}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<hr />
<h4>{% trans %}Events{% endtrans %}</h4>
{% set events = object_list.filter(type="EVENT", dates__end_date__gte=timezone.now()).distinct().order_by('id') %}
<h5>{% trans %}Displayed events{% endtrans %}</h5>
<table>
<thead>
<tr>
<td>{% trans %}Type{% endtrans %}</td>
<td>{% trans %}Title{% endtrans %}</td>
<td>{% trans %}Summary{% endtrans %}</td>
<td>{% trans %}Club{% endtrans %}</td>
<td>{% trans %}Author{% endtrans %}</td>
<td>{% trans %}Moderator{% endtrans %}</td>
<td>{% trans %}Start{% endtrans %}</td>
<td>{% trans %}End{% endtrans %}</td>
<td>{% trans %}Actions{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for news in events.filter(is_moderated=True) %}
<tr>
<td>{{ news.get_type_display() }}</td>
<td>{{ news.title }}</td>
<td>{{ news.summary|markdown }}</td>
<td><a href="{{ news.club.get_absolute_url() }}">{{ news.club }}</a></td>
<td>{{ user_profile_link(news.author) }}</td>
<td>{{ user_profile_link(news.moderator) }}</td>
<td>{{ news.dates.first().start_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().start_date|localtime|time(DATETIME_FORMAT) }}</td>
<td>{{ news.dates.first().end_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().end_date|localtime|time(DATETIME_FORMAT) }}</td>
<td><a href="{{ url('com:news_detail', news_id=news.id) }}">{% trans %}View{% endtrans %}</a>
<a href="{{ url('com:news_edit', news_id=news.id) }}">{% trans %}Edit{% endtrans %}</a>
<a href="{{ url('com:news_moderate', news_id=news.id) }}?remove">{% trans %}Remove{% endtrans %}</a>
<a href="{{ url('com:news_delete', news_id=news.id) }}">{% trans %}Delete{% endtrans %}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<h5>{% trans %}Events to moderate{% endtrans %}</h5>
<table>
<thead>
<tr>
<td>{% trans %}Type{% endtrans %}</td>
<td>{% trans %}Title{% endtrans %}</td>
<td>{% trans %}Summary{% endtrans %}</td>
<td>{% trans %}Club{% endtrans %}</td>
<td>{% trans %}Author{% endtrans %}</td>
<td>{% trans %}Start{% endtrans %}</td>
<td>{% trans %}End{% endtrans %}</td>
<td>{% trans %}Actions{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for news in events.filter(is_moderated=False) %}
<tr>
<td>{{ news.get_type_display() }}</td>
<td>{{ news.title }}</td>
<td>{{ news.summary|markdown }}</td>
<td><a href="{{ news.club.get_absolute_url() }}">{{ news.club }}</a></td>
<td>{{ user_profile_link(news.author) }}</td>
<td>{{ news.dates.first().start_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().start_date|localtime|time(DATETIME_FORMAT) }}</td>
<td>{{ news.dates.first().end_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().end_date|localtime|time(DATETIME_FORMAT) }}</td>
<td><a href="{{ url('com:news_detail', news_id=news.id) }}">{% trans %}View{% endtrans %}</a>
<a href="{{ url('com:news_edit', news_id=news.id) }}">{% trans %}Edit{% endtrans %}</a>
<a href="{{ url('com:news_moderate', news_id=news.id) }}">{% trans %}Moderate{% endtrans %}</a>
<a href="{{ url('com:news_delete', news_id=news.id) }}">{% trans %}Delete{% endtrans %}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<hr />
<h4>{% trans %}Events{% endtrans %}</h4>
{% set events = object_list.filter(type="EVENT", dates__end_date__gte=timezone.now()).distinct().order_by('id') %}
<h5>{% trans %}Displayed events{% endtrans %}</h5>
<table>
<thead>
<tr>
<td>{% trans %}Type{% endtrans %}</td>
<td>{% trans %}Title{% endtrans %}</td>
<td>{% trans %}Summary{% endtrans %}</td>
<td>{% trans %}Club{% endtrans %}</td>
<td>{% trans %}Author{% endtrans %}</td>
<td>{% trans %}Moderator{% endtrans %}</td>
<td>{% trans %}Start{% endtrans %}</td>
<td>{% trans %}End{% endtrans %}</td>
<td>{% trans %}Actions{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for news in events.filter(is_moderated=True) %}
<tr>
<td>{{ news.get_type_display() }}</td>
<td>{{ news.title }}</td>
<td>{{ news.summary|markdown }}</td>
<td><a href="{{ news.club.get_absolute_url() }}">{{ news.club }}</a></td>
<td>{{ user_profile_link(news.author) }}</td>
<td>{{ user_profile_link(news.moderator) }}</td>
<td>{{ news.dates.first().start_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().start_date|localtime|time(DATETIME_FORMAT) }}</td>
<td>{{ news.dates.first().end_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().end_date|localtime|time(DATETIME_FORMAT) }}</td>
<td><a href="{{ url('com:news_detail', news_id=news.id) }}">{% trans %}View{% endtrans %}</a>
<a href="{{ url('com:news_edit', news_id=news.id) }}">{% trans %}Edit{% endtrans %}</a>
<a href="{{ url('com:news_moderate', news_id=news.id) }}?remove">{% trans %}Remove{% endtrans %}</a>
<a href="{{ url('com:news_delete', news_id=news.id) }}">{% trans %}Delete{% endtrans %}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<h5>{% trans %}Events to moderate{% endtrans %}</h5>
<table>
<thead>
<tr>
<td>{% trans %}Type{% endtrans %}</td>
<td>{% trans %}Title{% endtrans %}</td>
<td>{% trans %}Summary{% endtrans %}</td>
<td>{% trans %}Club{% endtrans %}</td>
<td>{% trans %}Author{% endtrans %}</td>
<td>{% trans %}Start{% endtrans %}</td>
<td>{% trans %}End{% endtrans %}</td>
<td>{% trans %}Actions{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for news in events.filter(is_moderated=False) %}
<tr>
<td>{{ news.get_type_display() }}</td>
<td>{{ news.title }}</td>
<td>{{ news.summary|markdown }}</td>
<td><a href="{{ news.club.get_absolute_url() }}">{{ news.club }}</a></td>
<td>{{ user_profile_link(news.author) }}</td>
<td>{{ news.dates.first().start_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().start_date|localtime|time(DATETIME_FORMAT) }}</td>
<td>{{ news.dates.first().end_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().end_date|localtime|time(DATETIME_FORMAT) }}</td>
<td><a href="{{ url('com:news_detail', news_id=news.id) }}">{% trans %}View{% endtrans %}</a>
<a href="{{ url('com:news_edit', news_id=news.id) }}">{% trans %}Edit{% endtrans %}</a>
<a href="{{ url('com:news_moderate', news_id=news.id) }}">{% trans %}Moderate{% endtrans %}</a>
<a href="{{ url('com:news_delete', news_id=news.id) }}">{% trans %}Delete{% endtrans %}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}

View File

@ -2,48 +2,48 @@
{% from 'core/macros.jinja' import user_profile_link, facebook_share, tweet, link_news_logo, gen_news_metatags %}
{% block title %}
{% trans %}News{% endtrans %} -
{{ object.title }}
{% trans %}News{% endtrans %} -
{{ object.title }}
{% endblock %}
{% block head %}
{{ super() }}
{{ gen_news_metatags(news) }}
{{ super() }}
{{ gen_news_metatags(news) }}
{% endblock %}
{% block content %}
<p><a href="{{ url('com:news_list') }}">{% trans %}Back to news{% endtrans %}</a></p>
<section id="news_details">
<div class="club_logo">
<img src="{{ link_news_logo(news)}}" alt="{{ news.club }}" />
<a href="{{ news.club.get_absolute_url() }}">{{ news.club }}</a>
</div>
<h4>{{ news.title }}</h4>
<p class="date">
<span>{{ news.dates.first().start_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().start_date|localtime|time(DATETIME_FORMAT) }}</span> -
<span>{{ news.dates.first().end_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().end_date|localtime|time(DATETIME_FORMAT) }}</span>
</p>
<div class="news_content">
<div><em>{{ news.summary|markdown }}</em></div>
<br/>
<div>{{ news.content|markdown }}</div>
{{ facebook_share(news) }}
{{ tweet(news) }}
<div class="news_meta">
<p><a href="{{ url('com:news_list') }}">{% trans %}Back to news{% endtrans %}</a></p>
<section id="news_details">
<div class="club_logo">
<img src="{{ link_news_logo(news)}}" alt="{{ news.club }}" />
<a href="{{ news.club.get_absolute_url() }}">{{ news.club }}</a>
</div>
<h4>{{ news.title }}</h4>
<p class="date">
<span>{{ news.dates.first().start_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().start_date|localtime|time(DATETIME_FORMAT) }}</span> -
<span>{{ news.dates.first().end_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().end_date|localtime|time(DATETIME_FORMAT) }}</span>
</p>
<div class="news_content">
<div><em>{{ news.summary|markdown }}</em></div>
<br/>
<div>{{ news.content|markdown }}</div>
{{ facebook_share(news) }}
{{ tweet(news) }}
<div class="news_meta">
<p>{% trans %}Author: {% endtrans %}{{ user_profile_link(news.author) }}</p>
{% if news.moderator %}
<p>{% trans %}Moderator: {% endtrans %}{{ user_profile_link(news.moderator) }}</p>
<p>{% trans %}Moderator: {% endtrans %}{{ user_profile_link(news.moderator) }}</p>
{% elif user.is_com_admin %}
<p> <a href="{{ url('com:news_moderate', news_id=news.id) }}">{% trans %}Moderate{% endtrans %}</a></p>
<p> <a href="{{ url('com:news_moderate', news_id=news.id) }}">{% trans %}Moderate{% endtrans %}</a></p>
{% endif %}
{% if user.can_edit(news) %}
<p> <a href="{{ url('com:news_edit', news_id=news.id) }}">{% trans %}Edit (will be moderated again){% endtrans %}</a></p>
<p> <a href="{{ url('com:news_edit', news_id=news.id) }}">{% trans %}Edit (will be moderated again){% endtrans %}</a></p>
{% endif %}
</div>
</div>
</section>
</div>
</div>
</section>
{% endblock %}

View File

@ -2,46 +2,46 @@
{% from 'core/macros.jinja' import user_profile_link %}
{% block title %}
{% if object %}
{% trans %}Edit news{% endtrans %}
{% else %}
{% trans %}Create news{% endtrans %}
{% endif %}
{% if object %}
{% trans %}Edit news{% endtrans %}
{% else %}
{% trans %}Create news{% endtrans %}
{% endif %}
{% endblock %}
{% block content %}
{% if 'preview' in request.POST.keys() %}
<section class="news_event">
<h4>{{ form.instance.title }}</h4>
<p class="date">
{% if 'preview' in request.POST.keys() %}
<section class="news_event">
<h4>{{ form.instance.title }}</h4>
<p class="date">
<span>{{ form.instance.dates.first().start_date|localtime|date(DATETIME_FORMAT) }}
{{ form.instance.dates.first().start_date|localtime|time(DATETIME_FORMAT) }}</span> -
{{ form.instance.dates.first().start_date|localtime|time(DATETIME_FORMAT) }}</span> -
<span>{{ form.instance.dates.first().end_date|localtime|date(DATETIME_FORMAT) }}
{{ form.instance.dates.first().end_date|localtime|time(DATETIME_FORMAT) }}</span>
</p>
<p><a href="#">{{ form.instance.club or "Club" }}</a></p>
<div>{{ form.instance.summary|markdown }}</div>
<div>{{ form.instance.content|markdown }}</div>
<p>{% trans %}Author: {% endtrans %} {{ user_profile_link(form.instance.author) }}</p>
</section>
{% endif %}
{% if object %}
<h2>{% trans %}Edit news{% endtrans %}</h2>
{% else %}
<h2>{% trans %}Create news{% endtrans %}</h2>
{% endif %}
<form action="" method="post">
{{ form.instance.dates.first().end_date|localtime|time(DATETIME_FORMAT) }}</span>
</p>
<p><a href="#">{{ form.instance.club or "Club" }}</a></p>
<div>{{ form.instance.summary|markdown }}</div>
<div>{{ form.instance.content|markdown }}</div>
<p>{% trans %}Author: {% endtrans %} {{ user_profile_link(form.instance.author) }}</p>
</section>
{% endif %}
{% if object %}
<h2>{% trans %}Edit news{% endtrans %}</h2>
{% else %}
<h2>{% trans %}Create news{% endtrans %}</h2>
{% endif %}
<form action="" method="post">
{% csrf_token %}
{{ form.non_field_errors() }}
{{ form.author }}
<p>{{ form.type.errors }}<label for="{{ form.type.name }}">{{ form.type.label }}</label>
<ul>
<ul>
<li>{% trans %}Notice: Information, election result - no date{% endtrans %}</li>
<li>{% trans %}Event: punctual event, associated with one date{% endtrans %}</li>
<li>{% trans %}Weekly: recurrent event, associated with many dates (specify the first one, and a deadline){% endtrans %}</li>
<li>{% trans %}Call: long time event, associated with a long date (election appliance, ...){% endtrans %}</li>
</ul>
{{ form.type }}</p>
</ul>
{{ form.type }}</p>
<p class="date">{{ form.start_date.errors }}<label for="{{ form.start_date.name }}">{{ form.start_date.label }}</label> {{ form.start_date }}</p>
<p class="date">{{ form.end_date.errors }}<label for="{{ form.end_date.name }}">{{ form.end_date.label }}</label> {{ form.end_date }}</p>
<p class="until">{{ form.until.errors }}<label for="{{ form.until.name }}">{{ form.until.label }}</label> {{ form.until }}</p>
@ -50,38 +50,38 @@
<p>{{ form.summary.errors }}<label for="{{ form.summary.name }}">{{ form.summary.label }}</label> {{ form.summary }}</p>
<p>{{ form.content.errors }}<label for="{{ form.content.name }}">{{ form.content.label }}</label> {{ form.content }}</p>
{% if user.is_com_admin %}
<p>{{ form.automoderation.errors }}<label for="{{ form.automoderation.name }}">{{ form.automoderation.label }}</label>
<p>{{ form.automoderation.errors }}<label for="{{ form.automoderation.name }}">{{ form.automoderation.label }}</label>
{{ form.automoderation }}</p>
{% endif %}
<p><input type="submit" name="preview" value="{% trans %}Preview{% endtrans %}" /></p>
<p><input type="submit" value="{% trans %}Save{% endtrans %}" /></p>
</form>
</form>
{% endblock %}
{% block script %}
{{ super() }}
<script>
$( function() {
var type = $('input[name=type]');
var dates = $('.date');
var until = $('.until');
function update_targets () {
type_checked = $('input[name=type]:checked');
if (type_checked.val() == "EVENT" || type_checked.val() == "CALL") {
dates.show();
until.hide();
} else if (type_checked.val() == "WEEKLY") {
dates.show();
until.show();
} else {
dates.hide();
until.hide();
}
{{ super() }}
<script>
$( function() {
var type = $('input[name=type]');
var dates = $('.date');
var until = $('.until');
function update_targets () {
type_checked = $('input[name=type]:checked');
if (type_checked.val() == "EVENT" || type_checked.val() == "CALL") {
dates.show();
until.hide();
} else if (type_checked.val() == "WEEKLY") {
dates.show();
until.show();
} else {
dates.hide();
until.hide();
}
update_targets();
type.change(update_targets);
} );
</script>
}
update_targets();
type.change(update_targets);
} );
</script>
{% endblock %}

View File

@ -2,162 +2,162 @@
{% from 'core/macros.jinja' import tweet_quick, fb_quick %}
{% block title %}
{% trans %}News{% endtrans %}
{% trans %}News{% endtrans %}
{% endblock %}
{% block content %}
{% if user.is_com_admin %}
<div id="news_admin">
<a class="button" href="{{ url('com:news_admin_list') }}">{% trans %}Administrate news{% endtrans %}</a>
</div>
<br>
{% endif %}
{% if user.is_com_admin %}
<div id="news_admin">
<a class="button" href="{{ url('com:news_admin_list') }}">{% trans %}Administrate news{% endtrans %}</a>
</div>
<br>
{% endif %}
<div id="news">
<div id="news">
<div id="left_column" class="news_column">
{% for news in object_list.filter(type="NOTICE") %}
<section class="news_notice">
<h4><a href="{{ url('com:news_detail', news_id=news.id) }}">{{ news.title }}</a></h4>
<div class="news_content">{{ news.summary|markdown }}</div>
</section>
{% endfor %}
{% for news in object_list.filter(type="NOTICE") %}
<section class="news_notice">
<h4><a href="{{ url('com:news_detail', news_id=news.id) }}">{{ news.title }}</a></h4>
<div class="news_content">{{ news.summary|markdown }}</div>
</section>
{% endfor %}
{% for news in object_list.filter(dates__start_date__lte=timezone.now(), dates__end_date__gte=timezone.now(), type="CALL") %}
<section class="news_call">
{% for news in object_list.filter(dates__start_date__lte=timezone.now(), dates__end_date__gte=timezone.now(), type="CALL") %}
<section class="news_call">
<h4> <a href="{{ url('com:news_detail', news_id=news.id) }}">{{ news.title }}</a></h4>
<div class="news_date">
<span>{{ news.dates.first().start_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().start_date|localtime|time(DATETIME_FORMAT) }}</span> -
<span>{{ news.dates.first().end_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().end_date|localtime|time(DATETIME_FORMAT) }}</span>
</div>
<div class="news_content">{{ news.summary|markdown }}</div>
</section>
{% endfor %}
{% set events_dates = NewsDate.objects.filter(end_date__gte=timezone.now(), start_date__lte=timezone.now()+timedelta(days=5), news__type="EVENT", news__is_moderated=True).datetimes('start_date', 'day') %}
<h3>{% trans %}Events today and the next few days{% endtrans %}</h3>
{% if events_dates %}
{% for d in events_dates %}
<div class="news_events_group">
<div class="news_events_group_date">
<div>
<div>{{ d|localtime|date('D') }}</div>
<div class="day">{{ d|localtime|date('d') }}</div>
<div>{{ d|localtime|date('b') }}</div>
</div>
</div>
<div class="news_events_group_items">
{% for news in object_list.filter(dates__start_date__gte=d,
dates__start_date__lte=d+timedelta(days=1),
type="EVENT").exclude(dates__end_date__lt=timezone.now())
.order_by('dates__start_date') %}
<section class="news_event">
<div class="club_logo">
{% if news.club.logo %}
<img src="{{ news.club.logo.url }}" alt="{{ news.club }}" />
{% else %}
<img src="{{ static("com/img/news.png") }}" alt="{{ news.club }}" />
{% endif %}
</div>
<h4> <a href="{{ url('com:news_detail', news_id=news.id) }}">{{ news.title }}</a></h4>
<div><a href="{{ news.club.get_absolute_url() }}">{{ news.club }}</a></div>
<div class="news_date">
<span>{{ news.dates.first().start_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().start_date|localtime|time(DATETIME_FORMAT) }}</span> -
<span>{{ news.dates.first().end_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().end_date|localtime|time(DATETIME_FORMAT) }}</span>
<span>{{ news.dates.first().start_date|localtime|time(DATETIME_FORMAT) }}</span> -
<span>{{ news.dates.first().end_date|localtime|time(DATETIME_FORMAT) }}</span>
</div>
<div class="news_content">{{ news.summary|markdown }}</div>
</section>
<div class="news_content">{{ news.summary|markdown }}
<div class="button_bar">
{{ fb_quick(news) }}
{{ tweet_quick(news) }}
</div>
</div>
</section>
{% endfor %}
{% set events_dates = NewsDate.objects.filter(end_date__gte=timezone.now(), start_date__lte=timezone.now()+timedelta(days=5), news__type="EVENT", news__is_moderated=True).datetimes('start_date', 'day') %}
<h3>{% trans %}Events today and the next few days{% endtrans %}</h3>
{% if events_dates %}
{% for d in events_dates %}
<div class="news_events_group">
<div class="news_events_group_date">
<div>
<div>{{ d|localtime|date('D') }}</div>
<div class="day">{{ d|localtime|date('d') }}</div>
<div>{{ d|localtime|date('b') }}</div>
</div>
</div>
<div class="news_events_group_items">
{% for news in object_list.filter(dates__start_date__gte=d,
dates__start_date__lte=d+timedelta(days=1),
type="EVENT").exclude(dates__end_date__lt=timezone.now())
.order_by('dates__start_date') %}
<section class="news_event">
<div class="club_logo">
{% if news.club.logo %}
<img src="{{ news.club.logo.url }}" alt="{{ news.club }}" />
{% else %}
<img src="{{ static("com/img/news.png") }}" alt="{{ news.club }}" />
{% endif %}
</div>
<h4> <a href="{{ url('com:news_detail', news_id=news.id) }}">{{ news.title }}</a></h4>
<div><a href="{{ news.club.get_absolute_url() }}">{{ news.club }}</a></div>
<div class="news_date">
<span>{{ news.dates.first().start_date|localtime|time(DATETIME_FORMAT) }}</span> -
<span>{{ news.dates.first().end_date|localtime|time(DATETIME_FORMAT) }}</span>
</div>
<div class="news_content">{{ news.summary|markdown }}
<div class="button_bar">
{{ fb_quick(news) }}
{{ tweet_quick(news) }}
</div>
</div>
</section>
{% endfor %}
</div>
</div>
{% endfor %}
{% else %}
<div class="news_empty">
<em>{% trans %}Nothing to come...{% endtrans %}</em>
</div>
{% endif %}
{% set coming_soon = object_list.filter(dates__start_date__gte=timezone.now()+timedelta(days=5),
type="EVENT").order_by('dates__start_date') %}
{% if coming_soon %}
<h3>{% trans %}Coming soon... don't miss!{% endtrans %}</h3>
{% for news in coming_soon %}
<section class="news_coming_soon">
<a href="{{ url('com:news_detail', news_id=news.id) }}">{{ news.title }}</a>
<span class="news_date">{{ news.dates.first().start_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().start_date|localtime|time(DATETIME_FORMAT) }} -
{{ news.dates.first().end_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().end_date|localtime|time(DATETIME_FORMAT) }}</span>
</section>
{% endfor %}
{% endif %}
<h3>{% trans %}All coming events{% endtrans %}</h3>
<iframe
src="https://embed.styledcalendar.com/#2mF2is8CEXhr4ADcX6qN"
title="Styled Calendar"
class="styled-calendar-container"
style="width: 100%; border: none; height: 1060px"
data-cy="calendar-embed-iframe">
</iframe>
</div>
<div id="right_column" class="news_column">
<div id="agenda">
<div id="agenda_title">{% trans %}Agenda{% endtrans %}</div>
<div id="agenda_content">
{% for d in NewsDate.objects.filter(end_date__gte=timezone.now(),
news__is_moderated=True, news__type__in=["WEEKLY",
"EVENT"]).order_by('start_date', 'end_date') %}
<div class="agenda_item">
<div class="agenda_date">
<strong>{{ d.start_date|localtime|date('D d M Y') }}</strong>
</div>
<div class="agenda_time">
<span>{{ d.start_date|localtime|time(DATETIME_FORMAT) }}</span> -
<span>{{ d.end_date|localtime|time(DATETIME_FORMAT) }}</span>
</div>
<div>
<strong><a href="{{ url('com:news_detail', news_id=d.news.id) }}">{{ d.news.title }}</a></strong>
<a href="{{ d.news.club.get_absolute_url() }}">{{ d.news.club }}</a>
</div>
<div class="agenda_item_content">{{ d.news.summary|markdown }}</div>
</div>
{% endfor %}
</div>
</div>
</div>
{% endfor %}
{% else %}
<div class="news_empty">
<em>{% trans %}Nothing to come...{% endtrans %}</em>
</div>
{% endif %}
<div id="birthdays">
<div id="birthdays_title">{% trans %}Birthdays{% endtrans %}</div>
<div id="birthdays_content">
{% if user.is_subscribed %}
{% set coming_soon = object_list.filter(dates__start_date__gte=timezone.now()+timedelta(days=5),
type="EVENT").order_by('dates__start_date') %}
{% if coming_soon %}
<h3>{% trans %}Coming soon... don't miss!{% endtrans %}</h3>
{% for news in coming_soon %}
<section class="news_coming_soon">
<a href="{{ url('com:news_detail', news_id=news.id) }}">{{ news.title }}</a>
<span class="news_date">{{ news.dates.first().start_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().start_date|localtime|time(DATETIME_FORMAT) }} -
{{ news.dates.first().end_date|localtime|date(DATETIME_FORMAT) }}
{{ news.dates.first().end_date|localtime|time(DATETIME_FORMAT) }}</span>
</section>
{% endfor %}
{% endif %}
<h3>{% trans %}All coming events{% endtrans %}</h3>
<iframe
src="https://embed.styledcalendar.com/#2mF2is8CEXhr4ADcX6qN"
title="Styled Calendar"
class="styled-calendar-container"
style="width: 100%; border: none; height: 1060px"
data-cy="calendar-embed-iframe">
</iframe>
</div>
<div id="right_column" class="news_column">
<div id="agenda">
<div id="agenda_title">{% trans %}Agenda{% endtrans %}</div>
<div id="agenda_content">
{% for d in NewsDate.objects.filter(end_date__gte=timezone.now(),
news__is_moderated=True, news__type__in=["WEEKLY",
"EVENT"]).order_by('start_date', 'end_date') %}
<div class="agenda_item">
<div class="agenda_date">
<strong>{{ d.start_date|localtime|date('D d M Y') }}</strong>
</div>
<div class="agenda_time">
<span>{{ d.start_date|localtime|time(DATETIME_FORMAT) }}</span> -
<span>{{ d.end_date|localtime|time(DATETIME_FORMAT) }}</span>
</div>
<div>
<strong><a href="{{ url('com:news_detail', news_id=d.news.id) }}">{{ d.news.title }}</a></strong>
<a href="{{ d.news.club.get_absolute_url() }}">{{ d.news.club }}</a>
</div>
<div class="agenda_item_content">{{ d.news.summary|markdown }}</div>
</div>
{% endfor %}
</div>
</div>
<div id="birthdays">
<div id="birthdays_title">{% trans %}Birthdays{% endtrans %}</div>
<div id="birthdays_content">
{% if user.is_subscribed %}
{# Cache request for 1 hour #}
{% cache 3600 "birthdays" %}
<ul class="birthdays_year">
{% for d in birthdays.dates('date_of_birth', 'year', 'DESC') %}
<li>
{% trans age=timezone.now().year - d.year %}{{ age }} year old{% endtrans %}
<ul>
{% for u in birthdays.filter(date_of_birth__year=d.year) %}
<li><a href="{{ u.get_absolute_url() }}">{{ u.get_short_name() }}</a></li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
{% endcache %}
{% else %}
<p>{% trans %}You need an up to date subscription to access this content{% endtrans %}</p>
{% endif %}
</div>
</div>
{% cache 3600 "birthdays" %}
<ul class="birthdays_year">
{% for d in birthdays.dates('date_of_birth', 'year', 'DESC') %}
<li>
{% trans age=timezone.now().year - d.year %}{{ age }} year old{% endtrans %}
<ul>
{% for u in birthdays.filter(date_of_birth__year=d.year) %}
<li><a href="{{ u.get_absolute_url() }}">{{ u.get_short_name() }}</a></li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
{% endcache %}
{% else %}
<p>{% trans %}You need an up to date subscription to access this content{% endtrans %}</p>
{% endif %}
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -1,42 +1,42 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}Poster{% endtrans %}
{% trans %}Poster{% endtrans %}
{% endblock %}
{% block content %}
<div id="poster_edit">
<div id="poster_edit">
<div id="title">
<div id="links" class="left">
{% if app == "com" %}
<a id="list" class="link" href="{{ url(app + ":poster_list") }}">{% trans %}List{% endtrans %}</a>
{% elif app == "club" %}
<a id="list" class="link" href="{{ url(app + ":poster_list", club.id) }}">{% trans %}List{% endtrans %}</a>
{% endif %}
</div>
<h3>{% trans %}Posters - edit{% endtrans %}</h3>
<div id="links" class="right">
{% if app == "com" %}
<a class="link delete" href="{{ url(app + ":poster_delete", poster.id) }}">{% trans %}Delete{% endtrans %}</a>
{% elif app == "club" %}
<a class="link delete" href="{{ url(app + ":poster_delete", club.id, poster.id) }}">{% trans %}Delete{% endtrans %}</a>
{% endif %}
</div>
<div id="links" class="left">
{% if app == "com" %}
<a id="list" class="link" href="{{ url(app + ":poster_list") }}">{% trans %}List{% endtrans %}</a>
{% elif app == "club" %}
<a id="list" class="link" href="{{ url(app + ":poster_list", club.id) }}">{% trans %}List{% endtrans %}</a>
{% endif %}
</div>
<h3>{% trans %}Posters - edit{% endtrans %}</h3>
<div id="links" class="right">
{% if app == "com" %}
<a class="link delete" href="{{ url(app + ":poster_delete", poster.id) }}">{% trans %}Delete{% endtrans %}</a>
{% elif app == "club" %}
<a class="link delete" href="{{ url(app + ":poster_delete", club.id, poster.id) }}">{% trans %}Delete{% endtrans %}</a>
{% endif %}
</div>
</div>
<div id="poster">
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p() }}
<p><input type="submit" value="{% trans %}Save{% endtrans %}" /></p>
</form>
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p() }}
<p><input type="submit" value="{% trans %}Save{% endtrans %}" /></p>
</form>
</div>
</div>
</div>
{% endblock %}

View File

@ -1,66 +1,66 @@
{% extends "core/base.jinja" %}
{% block script %}
{{ super() }}
<script src="{{ static('com/js/poster_list.js') }}"></script>
{{ super() }}
<script src="{{ static('com/js/poster_list.js') }}"></script>
{% endblock %}
{% block title %}
{% trans %}Poster{% endtrans %}
{% trans %}Poster{% endtrans %}
{% endblock %}
{% block content %}
<div id="poster_list">
<div id="poster_list">
<div id="title">
<h3>{% trans %}Posters{% endtrans %}</h3>
<div id="links" class="right">
{% if app == "com" %}
<a id="create" class="link" href="{{ url(app + ":poster_create") }}">{% trans %}Create{% endtrans %}</a>
<a id="moderation" class="link" href="{{ url("com:poster_moderate_list") }}">{% trans %}Moderation{% endtrans %}</a>
{% elif app == "club" %}
<a id="create" class="link" href="{{ url(app + ":poster_create", club.id) }}">{% trans %}Create{% endtrans %}</a>
{% endif %}
</div>
<h3>{% trans %}Posters{% endtrans %}</h3>
<div id="links" class="right">
{% if app == "com" %}
<a id="create" class="link" href="{{ url(app + ":poster_create") }}">{% trans %}Create{% endtrans %}</a>
<a id="moderation" class="link" href="{{ url("com:poster_moderate_list") }}">{% trans %}Moderation{% endtrans %}</a>
{% elif app == "club" %}
<a id="create" class="link" href="{{ url(app + ":poster_create", club.id) }}">{% trans %}Create{% endtrans %}</a>
{% endif %}
</div>
</div>
<div id="posters">
{% if poster_list.count() == 0 %}
<div id="no-posters">{% trans %}No posters{% endtrans %}</div>
{% else %}
{% if poster_list.count() == 0 %}
<div id="no-posters">{% trans %}No posters{% endtrans %}</div>
{% else %}
{% for poster in poster_list %}
<div class="poster{% if not poster.is_moderated %} not_moderated{% endif %}">
<div class="name">{{ poster.name }}</div>
<div class="image"><img src="{{ poster.file.url }}"></img></div>
<div class="dates">
<div class="begin">{{ poster.date_begin | localtime | date("d/M/Y H:m") }}</div>
<div class="end">{{ poster.date_end | localtime | date("d/M/Y H:m") }}</div>
</div>
{% if app == "com" %}
<a class="edit" href="{{ url(app + ":poster_edit", poster.id) }}">{% trans %}Edit{% endtrans %}</a>
{% elif app == "club" %}
<a class="edit" href="{{ url(app + ":poster_edit", club.id, poster.id) }}">{% trans %}Edit{% endtrans %}</a>
{% endif %}
<div class="tooltip">
<ul>
{% for screen in poster.screens.all() %}
<li>{{ screen }}</li>
{% endfor %}
</ul>
</div>
</div>
{% endfor %}
{% for poster in poster_list %}
<div class="poster{% if not poster.is_moderated %} not_moderated{% endif %}">
<div class="name">{{ poster.name }}</div>
<div class="image"><img src="{{ poster.file.url }}"></img></div>
<div class="dates">
<div class="begin">{{ poster.date_begin | localtime | date("d/M/Y H:m") }}</div>
<div class="end">{{ poster.date_end | localtime | date("d/M/Y H:m") }}</div>
</div>
{% if app == "com" %}
<a class="edit" href="{{ url(app + ":poster_edit", poster.id) }}">{% trans %}Edit{% endtrans %}</a>
{% elif app == "club" %}
<a class="edit" href="{{ url(app + ":poster_edit", club.id, poster.id) }}">{% trans %}Edit{% endtrans %}</a>
{% endif %}
<div class="tooltip">
<ul>
{% for screen in poster.screens.all() %}
<li>{{ screen }}</li>
{% endfor %}
</ul>
</div>
</div>
{% endfor %}
{% endif %}
{% endif %}
</div>
<div id="view"><div id="placeholder"></div></div>
</div>
</div>
{% endblock %}

View File

@ -1,39 +1,39 @@
{% extends "core/base.jinja" %}
{% block script %}
{{ super() }}
<script src="{{ static('com/js/poster_list.js') }}"></script>
{{ super() }}
<script src="{{ static('com/js/poster_list.js') }}"></script>
{% endblock %}
{% block content %}
<div id="poster_list">
<div id="poster_list">
<div id="title">
<div id="links" class="left">
<a id="list" class="link" href="{{ url("com:poster_list") }}">{% trans %}List{% endtrans %}</a>
</div>
<h3>{% trans %}Posters - moderation{% endtrans %}</h3>
<div id="links" class="left">
<a id="list" class="link" href="{{ url("com:poster_list") }}">{% trans %}List{% endtrans %}</a>
</div>
<h3>{% trans %}Posters - moderation{% endtrans %}</h3>
</div>
<div id="posters">
{% if object_list.count == 0 %}
<div id="no-posters">{% trans %}No objects{% endtrans %}</div>
{% else %}
{% if object_list.count == 0 %}
<div id="no-posters">{% trans %}No objects{% endtrans %}</div>
{% else %}
{% for poster in object_list %}
<div class="poster{% if not poster.is_moderated %} not_moderated{% endif %}">
<div class="name"> {{ poster.name }} </div>
<div class="image"> <img src="{{ poster.file.url }}"></img> </div>
<a class="moderate" href="{{ url("com:poster_moderate", object_id=poster.id) }}">Moderate</a>
</div>
{% endfor %}
{% for poster in object_list %}
<div class="poster{% if not poster.is_moderated %} not_moderated{% endif %}">
<div class="name"> {{ poster.name }} </div>
<div class="image"> <img src="{{ poster.file.url }}"></img> </div>
<a class="moderate" href="{{ url("com:poster_moderate", object_id=poster.id) }}">Moderate</a>
</div>
{% endfor %}
{% endif %}
{% endif %}
</div>
<div id="view"><div id="placeholder"></div></div>
</div>
</div>
{% endblock %}

View File

@ -1,33 +1,33 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}Screen{% endtrans %}
{% trans %}Screen{% endtrans %}
{% endblock %}
{% block content %}
<div id="screen_edit">
<div id="screen_edit">
<div id="title">
<div id="links" class="left">
<a id="list" class="link" href="{{ url("com:screen_list") }}">{% trans %}List{% endtrans %}</a>
</div>
<h3>{% trans %}Screen - edit{% endtrans %}</h3>
<div id="links" class="right">
<a class="link delete" href="{{ url("com:screen_delete", screen.id) }}">{% trans %}Delete{% endtrans %}</a>
</div>
<div id="links" class="left">
<a id="list" class="link" href="{{ url("com:screen_list") }}">{% trans %}List{% endtrans %}</a>
</div>
<h3>{% trans %}Screen - edit{% endtrans %}</h3>
<div id="links" class="right">
<a class="link delete" href="{{ url("com:screen_delete", screen.id) }}">{% trans %}Delete{% endtrans %}</a>
</div>
</div>
<div id="screen">
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p() }}
<p><input type="submit" value="{% trans %}Save{% endtrans %}" /></p>
</form>
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p() }}
<p><input type="submit" value="{% trans %}Save{% endtrans %}" /></p>
</form>
</div>
</div>
</div>
{% endblock %}

View File

@ -1,38 +1,38 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}Screens{% endtrans %}
{% trans %}Screens{% endtrans %}
{% endblock %}
{% block content %}
<div id="screen_list">
<div id="screen_list">
<div id="title">
<h3>{% trans %}Screens{% endtrans %}</h3>
<div id="links" class="right">
<a id="create" class="link" href="{{ url("com:screen_create") }}">{% trans %}Create{% endtrans %}</a>
</div>
<h3>{% trans %}Screens{% endtrans %}</h3>
<div id="links" class="right">
<a id="create" class="link" href="{{ url("com:screen_create") }}">{% trans %}Create{% endtrans %}</a>
</div>
</div>
<div id="screens">
{% if screen_list.count() == 0 %}
<div id="no-screens">{% trans %}No screens{% endtrans %}</div>
{% else %}
{% if screen_list.count() == 0 %}
<div id="no-screens">{% trans %}No screens{% endtrans %}</div>
{% else %}
{% for screen in screen_list %}
<div class="screen">
<div class="name">{{ screen.name }}</div>
<a class="edit" href="{{ url("com:screen_edit", screen.id) }}">{% trans %}Edit{% endtrans %}</a>
<a class="slideshow" href="{{ url("com:screen_slideshow", screen.id) }}" target="_blank">{% trans %}Slideshow{% endtrans %}</a>
</div>
{% endfor %}
{% for screen in screen_list %}
<div class="screen">
<div class="name">{{ screen.name }}</div>
<a class="edit" href="{{ url("com:screen_edit", screen.id) }}">{% trans %}Edit{% endtrans %}</a>
<a class="slideshow" href="{{ url("com:screen_slideshow", screen.id) }}" target="_blank">{% trans %}Slideshow{% endtrans %}</a>
</div>
{% endfor %}
{% endif %}
{% endif %}
</div>
</div>
</div>
{% endblock %}

View File

@ -1,30 +1,30 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<head>
<title>{% trans %}Slideshow{% endtrans %}</title>
<link href="{{ scss('com/slideshow.scss') }}" rel="stylesheet" type="text/css" />
</head>
<body>
</head>
<body>
<div id="slideshow">
<div id="slides">
<div id="slides">
{% for poster in posters %}
<div class="slide {% if loop.first %}center{% else %}right{% endif %}" display_time="{{ poster.display_time }}">
<img src="{{ poster.file.url }}"></img>
</div>
<div class="slide {% if loop.first %}center{% else %}right{% endif %}" display_time="{{ poster.display_time }}">
<img src="{{ poster.file.url }}"></img>
</div>
{% endfor %}
</div>
</div>
<div id="progress_bullets">
<div id="progress_bullets">
{% for poster in posters %}
<div class="bullet {% if loop.first %}active{% endif %}"></div>
<div class="bullet {% if loop.first %}active{% endif %}"></div>
{% endfor %}
</div>
</div>
<div id="progress_bar"></div>
<div id="progress_bar"></div>
</div>
<script src="{{ static('core/js/jquery-3.6.2.min.js') }}"></script>
<script src="{{ static('com/js/slideshow.js') }}"></script>
</body>
</body>
</html>

View File

@ -2,77 +2,77 @@
{% from 'core/macros.jinja' import user_profile_link %}
{% block title %}
{% trans %}Weekmail{% endtrans %}
{% trans %}Weekmail{% endtrans %}
{% endblock %}
{% block content %}
<h3>{% trans %}Weekmail{% endtrans %} {{ object.id }}</h3>
<p><a href="{{ url('com:weekmail_preview') }}">{% trans %}Preview{% endtrans %}</a></p>
<p><a href="{{ url('com:weekmail_preview') }}?send=true">{% trans %}Send{% endtrans %}</a></p>
<p><a href="{{ url('com:weekmail_article') }}">{% trans %}New article{% endtrans %}</a></p>
<h4>{% trans %}Articles in no weekmail yet{% endtrans %}</h4>
<table>
<h3>{% trans %}Weekmail{% endtrans %} {{ object.id }}</h3>
<p><a href="{{ url('com:weekmail_preview') }}">{% trans %}Preview{% endtrans %}</a></p>
<p><a href="{{ url('com:weekmail_preview') }}?send=true">{% trans %}Send{% endtrans %}</a></p>
<p><a href="{{ url('com:weekmail_article') }}">{% trans %}New article{% endtrans %}</a></p>
<h4>{% trans %}Articles in no weekmail yet{% endtrans %}</h4>
<table>
<thead>
<tr>
<td>{% trans %}Author{% endtrans %}</td>
<td>{% trans %}Club{% endtrans %}</td>
<td>{% trans %}Title{% endtrans %}</td>
<td>{% trans %}Content{% endtrans %}</td>
<td>{% trans %}Actions{% endtrans %}</td>
</tr>
<tr>
<td>{% trans %}Author{% endtrans %}</td>
<td>{% trans %}Club{% endtrans %}</td>
<td>{% trans %}Title{% endtrans %}</td>
<td>{% trans %}Content{% endtrans %}</td>
<td>{% trans %}Actions{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for a in orphans.all() %}
{% for a in orphans.all() %}
<tr>
<td>{{ user_profile_link(a.author) }}</td>
<td><a href="{{ a.club.get_absolute_url() }}">{{ a.club }}</a></td>
<td>{{ a.title }}</td>
<td>{{ a.content|markdown }}</td>
<td>
<a href="{{ url('com:weekmail_article_edit', article_id=a.id) }}">{% trans %}Edit{% endtrans %}</a> |
<a href="{{ url('com:weekmail_article_delete', article_id=a.id) }}">{% trans %}Delete{% endtrans %}</a> |
<a href="?add_article={{ a.id }}">{% trans %}Add to weekmail{% endtrans %}</a> |
<a href="?up_article={{ a.id }}">{% trans %}Up{% endtrans %}</a> |
<a href="?down_article={{ a.id }}">{% trans %}Down{% endtrans %}</a>
</td>
<td>{{ user_profile_link(a.author) }}</td>
<td><a href="{{ a.club.get_absolute_url() }}">{{ a.club }}</a></td>
<td>{{ a.title }}</td>
<td>{{ a.content|markdown }}</td>
<td>
<a href="{{ url('com:weekmail_article_edit', article_id=a.id) }}">{% trans %}Edit{% endtrans %}</a> |
<a href="{{ url('com:weekmail_article_delete', article_id=a.id) }}">{% trans %}Delete{% endtrans %}</a> |
<a href="?add_article={{ a.id }}">{% trans %}Add to weekmail{% endtrans %}</a> |
<a href="?up_article={{ a.id }}">{% trans %}Up{% endtrans %}</a> |
<a href="?down_article={{ a.id }}">{% trans %}Down{% endtrans %}</a>
</td>
</tr>
{% endfor %}
{% endfor %}
</tbody>
</table>
<h4>{% trans %}Articles included the next weekmail{% endtrans %}</h4>
<table>
</table>
<h4>{% trans %}Articles included the next weekmail{% endtrans %}</h4>
<table>
<thead>
<tr>
<td>{% trans %}Author{% endtrans %}</td>
<td>{% trans %}Club{% endtrans %}</td>
<td>{% trans %}Title{% endtrans %}</td>
<td>{% trans %}Content{% endtrans %}</td>
<td>{% trans %}Actions{% endtrans %}</td>
</tr>
<tr>
<td>{% trans %}Author{% endtrans %}</td>
<td>{% trans %}Club{% endtrans %}</td>
<td>{% trans %}Title{% endtrans %}</td>
<td>{% trans %}Content{% endtrans %}</td>
<td>{% trans %}Actions{% endtrans %}</td>
</tr>
</thead>
<tbody>
{% for a in object.articles.order_by('rank') %}
{% for a in object.articles.order_by('rank') %}
<tr>
<td>{{ user_profile_link(a.author) }}</td>
<td><a href="{{ a.club.get_absolute_url() }}">{{ a.club }}</a></td>
<td>{{ a.title }}</td>
<td>{{ a.content|markdown }}</td>
<td>
<a href="{{ url('com:weekmail_article_edit', article_id=a.id) }}">{% trans %}Edit{% endtrans %}</a> |
<a href="{{ url('com:weekmail_article_delete', article_id=a.id) }}">{% trans %}Delete{% endtrans %}</a> |
<a href="?del_article={{ a.id }}">{% trans %}Delete from weekmail{% endtrans %}</a> |
<a href="?up_article={{ a.id }}">{% trans %}Up{% endtrans %}</a> |
<a href="?down_article={{ a.id }}">{% trans %}Down{% endtrans %}</a>
</td>
<td>{{ user_profile_link(a.author) }}</td>
<td><a href="{{ a.club.get_absolute_url() }}">{{ a.club }}</a></td>
<td>{{ a.title }}</td>
<td>{{ a.content|markdown }}</td>
<td>
<a href="{{ url('com:weekmail_article_edit', article_id=a.id) }}">{% trans %}Edit{% endtrans %}</a> |
<a href="{{ url('com:weekmail_article_delete', article_id=a.id) }}">{% trans %}Delete{% endtrans %}</a> |
<a href="?del_article={{ a.id }}">{% trans %}Delete from weekmail{% endtrans %}</a> |
<a href="?up_article={{ a.id }}">{% trans %}Up{% endtrans %}</a> |
<a href="?down_article={{ a.id }}">{% trans %}Down{% endtrans %}</a>
</td>
</tr>
{% endfor %}
{% endfor %}
</tbody>
</table>
<form action="" method="post" enctype="multipart/form-data">
</table>
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p() }}
<p><input type="submit" value="{% trans %}Save{% endtrans %}" /></p>
</form>
</form>
{% endblock %}

View File

@ -2,41 +2,41 @@
{% from 'core/macros.jinja' import user_profile_link %}
{% block title %}
{{ weekmail.title }}
{{ weekmail.title }}
{% endblock %}
{% block content %}
<a href="{{ url('com:weekmail') }}">{% trans %}Back{% endtrans %}</a>
{% if bad_recipients %}
<a href="{{ url('com:weekmail') }}">{% trans %}Back{% endtrans %}</a>
{% if bad_recipients %}
<p>
<span class="important">
<span class="important">
{% trans %}The following recipients were refused by the SMTP:{% endtrans %}
</span>
<ul>
{% for r in bad_recipients.keys() %}
<li>{{ r }}</li>
{% endfor %}
</ul>
</span>
<ul>
{% for r in bad_recipients.keys() %}
<li>{{ r }}</li>
{% endfor %}
</ul>
</p>
<form method="post" action="">
{% csrf_token %}
<button type="submit" name="send" value="clean">{% trans %}Clean subscribers{% endtrans %}</button>
{% csrf_token %}
<button type="submit" name="send" value="clean">{% trans %}Clean subscribers{% endtrans %}</button>
</form>
{% else %}
{% else %}
{% if request.GET['send'] %}
<p>{% trans %}Are you sure you want to send this weekmail?{% endtrans %}</p>
{% if request.LANGUAGE_CODE != settings.LANGUAGE_CODE[:2] %}
<p><strong>{% trans %}Warning: you are sending the weekmail in another language than the default one!{% endtrans %}</strong></p>
{% endif %}
<form method="post" action="">
{% csrf_token %}
<button type="submit" name="send" value="validate">{% trans %}Send{% endtrans %}</button>
</form>
<p>{% trans %}Are you sure you want to send this weekmail?{% endtrans %}</p>
{% if request.LANGUAGE_CODE != settings.LANGUAGE_CODE[:2] %}
<p><strong>{% trans %}Warning: you are sending the weekmail in another language than the default one!{% endtrans %}</strong></p>
{% endif %}
<form method="post" action="">
{% csrf_token %}
<button type="submit" name="send" value="validate">{% trans %}Send{% endtrans %}</button>
</form>
{% endif %}
{% endif %}
<hr>
{{ weekmail_rendered|safe }}
{% endif %}
<hr>
{{ weekmail_rendered|safe }}
{% endblock %}

View File

@ -1,52 +1,52 @@
<style type="text/css" media="all">
h1, h2, h3, h4, h5, h6, p {
h1, h2, h3, h4, h5, h6, p {
padding: 5px ;
margin: 5px;
}
img {
}
img {
margin: 5px ;
width: 95%;
}
}
</style>
<div style="background: #CBD1DD; padding: 0px 5%;">
<div style="background: #F9FAFB;">
<h2 style="background: #000; color: #f9fafb">{{ weekmail.title }}</h2>
<img src="{{ weekmail.get_banner() }}"
id="OWATemporaryImageDivContainerBannerForOutlook"
>
{% if weekmail.intro %}
<h3 style="background: #000; color: #f9fafb">{% trans %}Intro{% endtrans %}</h3>
{{ weekmail.intro|markdown }}
{% endif %}
<div style="background: #F9FAFB;">
<h2 style="background: #000; color: #f9fafb">{{ weekmail.title }}</h2>
<img src="{{ weekmail.get_banner() }}"
id="OWATemporaryImageDivContainerBannerForOutlook"
>
{% if weekmail.intro %}
<h3 style="background: #000; color: #f9fafb">{% trans %}Intro{% endtrans %}</h3>
{{ weekmail.intro|markdown }}
{% endif %}
<h3 style="background: #000; color: #f9fafb">{% trans %}Table of content{% endtrans %}</h3>
<ul>
{% for a in weekmail.articles.all() %}
<li>[{{ a.club }}] {{ a.title }}</li>
{%- endfor %}
</ul>
<h3 style="background: #000; color: #f9fafb">{% trans %}Table of content{% endtrans %}</h3>
<ul>
{% for a in weekmail.articles.all() %}
<li>[{{ a.club }}] {{ a.title }}</li>
{%- endfor %}
</ul>
{%- for a in weekmail.articles.all() %}
<h3 style="background: #000; color: #f9fafb">[{{ a.club }}] {{ a.title }}</h3>
{{ a.content|markdown }}
{%- endfor -%}
{%- for a in weekmail.articles.all() %}
<h3 style="background: #000; color: #f9fafb">[{{ a.club }}] {{ a.title }}</h3>
{{ a.content|markdown }}
{%- endfor -%}
{%- if weekmail.joke %}
<h3 style="background: #000; color: #f9fafb">{% trans %}Joke{% endtrans %}</h3>
{{ weekmail.joke|markdown }}
{% endif -%}
{%- if weekmail.joke %}
<h3 style="background: #000; color: #f9fafb">{% trans %}Joke{% endtrans %}</h3>
{{ weekmail.joke|markdown }}
{% endif -%}
{%- if weekmail.protip %}
<h3 style="background: #000; color: #f9fafb">{% trans %}Pro tip{% endtrans %}</h3>
{{ weekmail.protip|markdown }}
{% endif -%}
{%- if weekmail.protip %}
<h3 style="background: #000; color: #f9fafb">{% trans %}Pro tip{% endtrans %}</h3>
{{ weekmail.protip|markdown }}
{% endif -%}
{%- if weekmail.conclusion %}
<h3 style="background: #000; color: #f9fafb">{% trans %}Final word{% endtrans %}</h3>
{{ weekmail.conclusion|markdown }}
{% endif -%}
<img src="{{ weekmail.get_footer() }}"
</div>
{%- if weekmail.conclusion %}
<h3 style="background: #000; color: #f9fafb">{% trans %}Final word{% endtrans %}</h3>
{{ weekmail.conclusion|markdown }}
{% endif -%}
<img src="{{ weekmail.get_footer() }}"
</div>
</div>
</div>

View File

@ -1,8 +1,8 @@
# {{ weekmail.title }}
{%- if weekmail.intro %}
## {% trans %}Intro{% endtrans %}
{{ weekmail.intro }}
## {% trans %}Intro{% endtrans %}
{{ weekmail.intro }}
{% endif %}
## {% trans %}Table of content{% endtrans %}
@ -11,22 +11,22 @@
{% endfor -%}
{% for a in weekmail.articles.all() %}
## [{{ a.club }}] {{ a.title }}
{{ a.content }}
## [{{ a.club }}] {{ a.title }}
{{ a.content }}
{% endfor -%}
{%- if weekmail.joke %}
## {% trans %}Joke{% endtrans %}
{{ weekmail.joke }}
## {% trans %}Joke{% endtrans %}
{{ weekmail.joke }}
{% endif -%}
{%- if weekmail.protip %}
## {% trans %}Pro tip{% endtrans %}
{{ weekmail.protip }}
## {% trans %}Pro tip{% endtrans %}
{{ weekmail.protip }}
{% endif -%}
{%- if weekmail.conclusion %}
## {% trans %}Final word{% endtrans %}
{{ weekmail.conclusion }}
## {% trans %}Final word{% endtrans %}
{{ weekmail.conclusion }}
{% endif -%}

View File

@ -69,11 +69,11 @@ class TestCom(TestCase):
},
)
r = self.client.get(reverse("core:index"))
self.assertContains(
r,
"""<div id="alert_box">
<div class="markdown"><h3>ALERTE!</h3>
<p><strong>Caaaataaaapuuuulte!!!!</strong></p>""",
assert r.status_code == 200
self.assertInHTML(
"""<div id="alert_box"><div class="markdown"><h3>ALERTE!</h3>
<p><strong>Caaaataaaapuuuulte!!!!</strong></p>""",
r.content.decode(),
)
def test_info_msg(self):
@ -86,10 +86,12 @@ class TestCom(TestCase):
},
)
r = self.client.get(reverse("core:index"))
self.assertContains(
r,
"""<div id="info_box">
<div class="markdown"><h3>INFO: <strong>Caaaataaaapuuuulte!!!!</strong></h3>""",
assert r.status_code == 200
self.assertInHTML(
"""<div id="info_box"><div class="markdown">
<h3>INFO: <strong>Caaaataaaapuuuulte!!!!</strong></h3>""",
r.content.decode(),
)
def test_birthday_non_subscribed_user(self):

View File

@ -50,7 +50,7 @@ from core.views import (
QuickNotifMixin,
TabedViewMixin,
)
from core.views.forms import MarkdownInput, TzAwareDateTimeField
from core.views.forms import MarkdownInput, SelectDateTime
# Sith object
@ -72,12 +72,15 @@ class PosterForm(forms.ModelForm):
widgets = {"screens": forms.CheckboxSelectMultiple}
help_texts = {"file": _("Format: 16:9 | Resolution: 1920x1080")}
date_begin = TzAwareDateTimeField(
date_begin = forms.DateTimeField(
label=_("Start date"),
widget=SelectDateTime,
required=True,
initial=timezone.now().strftime("%Y-%m-%d %H:%M:%S"),
)
date_end = TzAwareDateTimeField(label=_("End date"), required=False)
date_end = forms.DateTimeField(
label=_("End date"), widget=SelectDateTime, required=False
)
def __init__(self, *args, **kwargs):
self.user = kwargs.pop("user", None)
@ -191,9 +194,13 @@ class NewsForm(forms.ModelForm):
"content": MarkdownInput,
}
start_date = TzAwareDateTimeField(label=_("Start date"), required=False)
end_date = TzAwareDateTimeField(label=_("End date"), required=False)
until = TzAwareDateTimeField(label=_("Until"), required=False)
start_date = forms.DateTimeField(
label=_("Start date"), widget=SelectDateTime, required=False
)
end_date = forms.DateTimeField(
label=_("End date"), widget=SelectDateTime, required=False
)
until = forms.DateTimeField(label=_("Until"), widget=SelectDateTime, required=False)
automoderation = forms.BooleanField(label=_("Automoderation"), required=False)
@ -258,7 +265,7 @@ class NewsEditView(CanEditMixin, UpdateView):
def post(self, request, *args, **kwargs):
form = self.get_form()
if form.is_valid() and "preview" not in request.POST.keys():
if form.is_valid() and "preview" not in request.POST:
return self.form_valid(form)
else:
return self.form_invalid(form)

View File

@ -21,8 +21,6 @@
#
#
import os
from django.core.management.base import BaseCommand
from core.models import SithFile
@ -37,9 +35,6 @@ class Command(BaseCommand):
)
def handle(self, *args, **options):
root_path = os.path.dirname(
os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
)
files = SithFile.objects.filter(id__in=options["ids"]).all()
for f in files:
f._check_fs()

View File

@ -22,7 +22,7 @@
#
#
import os
import sys
import sass
from django.conf import settings
@ -34,44 +34,36 @@ class Command(BaseCommand):
help = "Compile scss files from static folder"
def compile(self, filename):
args = {"filename": filename, "include_paths": settings.STATIC_ROOT}
def compile(self, filename: str):
args = {
"filename": filename,
"include_paths": settings.STATIC_ROOT.name,
"output_style": "compressed",
}
if settings.SASS_PRECISION:
args["precision"] = settings.SASS_PRECISION
return sass.compile(**args)
def is_compilable(self, file, ext_list):
path, ext = os.path.splitext(file)
return ext in ext_list
def exec_on_folder(self, folder, func):
to_exec = []
for file in os.listdir(folder):
file = os.path.join(folder, file)
if os.path.isdir(file):
self.exec_on_folder(file, func)
elif self.is_compilable(file, [".scss"]):
to_exec.append(file)
for file in to_exec:
func(file)
def compilescss(self, file):
print("compiling %s" % file)
with open(file.replace(".scss", ".css"), "w") as newfile:
newfile.write(self.compile(file))
def removescss(self, file):
print("removing %s" % file)
os.remove(file)
def handle(self, *args, **options):
if os.path.isdir(settings.STATIC_ROOT):
print("---- Compiling scss files ---")
self.exec_on_folder(settings.STATIC_ROOT, self.compilescss)
print("---- Removing scss files ----")
self.exec_on_folder(settings.STATIC_ROOT, self.removescss)
else:
print(
"No static folder avalaible, please use collectstatic before compiling scss"
if not settings.STATIC_ROOT.is_dir():
raise Exception(
"No static folder availaible, please use collectstatic before compiling scss"
)
to_exec = list(settings.STATIC_ROOT.rglob("*.scss"))
if len(to_exec) == 0:
self.stdout.write("Nothing to compile.")
sys.exit(0)
self.stdout.write("---- Compiling scss files ---")
for file in to_exec:
# remove existing css files that will be replaced
# keeping them while compiling the scss would break
# import statements resolution
css_file = file.with_suffix(".css")
if css_file.exists():
css_file.unlink()
compiled_files = {file: self.compile(str(file.resolve())) for file in to_exec}
for file, scss in compiled_files.items():
file.replace(file.with_suffix(".css")).write_text(scss)
self.stdout.write(
"Files compiled : \n" + "\n- ".join(str(f) for f in compiled_files)
)

View File

@ -21,7 +21,6 @@
#
#
from pathlib import Path
from django.conf import settings
from django.core.management.base import BaseCommand
@ -33,7 +32,7 @@ class Command(BaseCommand):
help = "Output the fully rendered SYNTAX.md file"
def handle(self, *args, **options):
root_path = Path(settings.BASE_DIR)
root_path = settings.BASE_DIR
with open(root_path / "core/fixtures/SYNTAX.md", "r") as md:
result = markdown(md.read())
print(result, end="")

View File

@ -0,0 +1,383 @@
import random
from datetime import date, timedelta
from decimal import Decimal
from typing import Iterator
from dateutil.relativedelta import relativedelta
from django.conf import settings
from django.core.management.base import BaseCommand
from django.db.models import Exists, F, Min, OuterRef, Subquery, Sum
from django.db.models.functions import Coalesce
from django.utils.timezone import make_aware, now
from faker import Faker
from club.models import Club, Membership
from core.models import RealGroup, User
from counter.models import (
Counter,
Customer,
Permanency,
Product,
ProductType,
Refilling,
Selling,
)
from pedagogy.models import UV
from subscription.models import Subscription
class Command(BaseCommand):
help = "Add more fixtures for a more complete development environment"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.faker = Faker("fr_FR")
def handle(self, *args, **options):
if not settings.DEBUG:
raise Exception("Never call this command in prod. Never.")
self.stdout.write("Creating users...")
users = [
User(
username=self.faker.user_name(),
first_name=self.faker.first_name(),
last_name=self.faker.last_name(),
date_of_birth=self.faker.date_of_birth(minimum_age=15, maximum_age=25),
email=self.faker.email(),
phone=self.faker.phone_number(),
address=self.faker.address(),
)
for _ in range(600)
]
# there may a duplicate or two
# Not a problem, we will just have 599 users instead of 600
User.objects.bulk_create(users, ignore_conflicts=True)
users = list(User.objects.order_by("-id")[: len(users)])
subscribers = random.sample(users, k=int(0.8 * len(users)))
self.stdout.write("Creating subscriptions...")
self.create_subscriptions(users)
self.stdout.write("Creating club memberships...")
users_qs = User.objects.filter(id__in=[s.id for s in subscribers])
subscribers_now = list(
users_qs.annotate(
filter=Exists(
Subscription.objects.filter(
member_id=OuterRef("pk"), subscription_end__gte=now()
)
)
)
)
old_subscribers = list(
users_qs.annotate(
filter=Exists(
Subscription.objects.filter(
member_id=OuterRef("pk"), subscription_end__lt=now()
)
)
)
)
self.make_club(
Club.objects.get(unix_name="ae"),
random.sample(subscribers_now, k=min(30, len(subscribers_now))),
random.sample(old_subscribers, k=min(60, len(old_subscribers))),
)
self.make_club(
Club.objects.get(unix_name="troll"),
random.sample(subscribers_now, k=min(20, len(subscribers_now))),
random.sample(old_subscribers, k=min(80, len(old_subscribers))),
)
self.stdout.write("Creating uvs...")
self.create_uvs()
self.stdout.write("Creating products...")
self.create_products()
self.stdout.write("Creating sales and refills...")
sellers = random.sample(list(User.objects.all()), 100)
self.create_sales(sellers)
self.stdout.write("Creating permanences...")
self.create_permanences(sellers)
self.stdout.write("Done")
def create_subscriptions(self, users: list[User]):
def prepare_subscription(user: User, start_date: date) -> Subscription:
payment_method = random.choice(settings.SITH_SUBSCRIPTION_PAYMENT_METHOD)[0]
duration = random.randint(1, 4)
sub = Subscription(member=user, payment_method=payment_method)
sub.subscription_start = sub.compute_start(d=start_date, duration=duration)
sub.subscription_end = sub.compute_end(duration)
return sub
subscriptions = []
customers = []
# first set of subscriptions
for i, user in enumerate(users):
sub = prepare_subscription(user, self.faker.past_date("-10y"))
subscriptions.append(sub)
customers.append(
Customer(
user=user,
account_id=f"{9900 + i}{self.faker.random_lowercase_letter()}",
)
)
while sub.subscription_end < now().date() and random.random() > 0.7:
# 70% chances to subscribe again
# (expect if it would make the subscription start after tomorrow)
sub = prepare_subscription(
user, self.faker.past_date(sub.subscription_end)
)
subscriptions.append(sub)
Subscription.objects.bulk_create(subscriptions)
Customer.objects.bulk_create(customers, ignore_conflicts=True)
def make_club(self, club: Club, members: list[User], old_members: list[User]):
def zip_roles(users: list[User]) -> Iterator[tuple[User, int]]:
roles = iter(sorted(settings.SITH_CLUB_ROLES.keys(), reverse=True))
user_idx = 0
while (role := next(roles)) > 2:
# one member for each major role
yield users[user_idx], role
user_idx += 1
for _ in range(int(0.3 * (len(users) - user_idx))):
# 30% of the remaining in the board
yield users[user_idx], 2
user_idx += 1
for remaining in users[user_idx + 1 :]:
# everything else is a simple member
yield remaining, 1
memberships = []
old_members = old_members.copy()
random.shuffle(old_members)
for old in old_members:
start = self.faker.date_between("-3y", "-1y")
memberships.append(
Membership(
start_date=start,
end_date=self.faker.past_date(start),
user=old,
role=random.choice(list(settings.SITH_CLUB_ROLES.keys())),
club=club,
)
)
for member, role in zip_roles(members):
start = self.faker.past_date("-1y")
memberships.append(
Membership(
start_date=start,
user=member,
role=role,
club=club,
)
)
Membership.objects.bulk_create(memberships)
def create_uvs(self):
root = User.objects.get(username="root")
categories = ["CS", "TM", "OM", "QC", "EC"]
branches = ["TC", "GMC", "GI", "EDIM", "E", "IMSI", "HUMA"]
languages = ["FR", "FR", "EN"]
semesters = ["AUTUMN", "SPRING", "AUTUMN_AND_SPRING"]
teachers = [self.faker.name() for _ in range(50)]
uvs = []
for _ in range(1000):
code = (
self.faker.random_uppercase_letter()
+ self.faker.random_uppercase_letter()
+ str(random.randint(10, 90))
)
uvs.append(
UV(
code=code,
author=root,
manager=random.choice(teachers),
title=self.faker.text(max_nb_chars=50),
department=random.choice(branches),
credit_type=random.choice(categories),
credits=6,
semester=random.choice(semesters),
language=random.choice(languages),
program=self.faker.paragraph(random.randint(3, 10)),
skills="\n* ".join(self.faker.sentences(random.randint(3, 10))),
key_concepts="\n* ".join(
self.faker.sentences(random.randint(3, 10))
),
hours_CM=random.randint(15, 40),
hours_TD=random.randint(15, 40),
hours_TP=random.randint(15, 40),
hours_THE=random.randint(15, 40),
hours_TE=random.randint(15, 40),
)
)
UV.objects.bulk_create(uvs, ignore_conflicts=True)
def create_products(self):
categories = []
for _ in range(10):
categories.append(ProductType(name=self.faker.text(max_nb_chars=30)))
ProductType.objects.bulk_create(categories)
categories = list(
ProductType.objects.filter(name__in=[c.name for c in categories])
)
ae = Club.objects.get(unix_name="ae")
other_clubs = random.sample(list(Club.objects.all()), k=3)
groups = list(
RealGroup.objects.filter(
name__in=["Subscribers", "Old subscribers", "Public"]
)
)
counters = list(
Counter.objects.filter(name__in=["Foyer", "MDE", "La Gommette", "Eboutic"])
)
# 2/3 of the products are owned by AE
clubs = [ae, ae, ae, ae, ae, ae, *other_clubs]
products = []
buying_groups = []
selling_places = []
for _ in range(200):
price = random.randint(0, 10) + random.choice([0, 0.25, 0.5, 0.75])
product = Product(
name=self.faker.text(max_nb_chars=30),
description=self.faker.text(max_nb_chars=120),
product_type=random.choice(categories),
code="".join(self.faker.random_letters(length=random.randint(4, 8))),
purchase_price=price,
selling_price=price,
special_selling_price=price - min(0.5, price),
club=random.choice(clubs),
limit_age=0 if random.random() > 0.2 else 18,
archived=bool(random.random() > 0.7),
)
products.append(product)
for group in random.sample(groups, k=random.randint(0, 3)):
# there will be products without buying groups
# but there are also such products in the real database
buying_groups.append(
Product.buying_groups.through(product=product, group=group)
)
for counter in random.sample(counters, random.randint(0, 4)):
selling_places.append(
Counter.products.through(counter=counter, product=product)
)
Product.objects.bulk_create(products)
Product.buying_groups.through.objects.bulk_create(buying_groups)
Counter.products.through.objects.bulk_create(selling_places)
@staticmethod
def _update_balances():
customers = Customer.objects.annotate(
money_in=Sum(F("refillings__amount"), default=0),
money_out=Coalesce(
Subquery(
Selling.objects.filter(customer=OuterRef("pk"))
.values("customer_id") # group by customer
.annotate(res=Sum(F("unit_price") * F("quantity"), default=0))
.values("res")
),
Decimal("0"),
),
).annotate(real_balance=F("money_in") - F("money_out"))
for c in customers:
c.amount = c.real_balance
Customer.objects.bulk_update(customers, fields=["amount"])
def create_sales(self, sellers: list[User]):
customers = list(
Customer.objects.annotate(
since=Subquery(
Subscription.objects.filter(member__customer=OuterRef("pk"))
.annotate(res=Min("subscription_start"))
.values("res")
)
)
)
products = list(Product.objects.all())
counters = list(
Counter.objects.filter(name__in=["Foyer", "MDE", "La Gommette"])
)
sales = []
reloads = []
for customer in customers:
# the longer the customer has existed, the higher the mean of nb_products
mu = 5 + (now().year - customer.since.year) * 2
nb_sales = max(0, int(random.normalvariate(mu=mu, sigma=mu * 5)))
favoured_products = random.sample(products, k=(random.randint(1, 5)))
favoured_counter = random.choice(counters)
this_customer_sales = []
for _ in range(nb_sales):
product = (
random.choice(favoured_products)
if random.random() > 0.7
else random.choice(products)
)
counter = (
favoured_counter
if random.random() > 0.7
else random.choice(counters)
)
this_customer_sales.append(
Selling(
product=product,
counter=counter,
club_id=product.club_id,
quantity=random.randint(1, 5),
unit_price=product.selling_price,
seller=random.choice(sellers),
customer=customer,
date=make_aware(
self.faker.date_time_between(customer.since, now().date())
),
)
)
total_expanse = sum(s.unit_price * s.quantity for s in this_customer_sales)
total_reloaded = 0
while total_reloaded < total_expanse:
amount = random.choice(list(range(5, 51, 5)))
total_reloaded += amount
reloads.append(
Refilling(
counter=random.choice(counters),
amount=amount,
operator=random.choice(sellers),
customer=customer,
date=make_aware(
self.faker.date_time_between(customer.since, now().date())
),
is_validated=True,
)
)
sales.extend(this_customer_sales)
Refilling.objects.bulk_create(reloads)
Selling.objects.bulk_create(sales)
self._update_balances()
def create_permanences(self, sellers: list[User]):
counters = list(
Counter.objects.filter(name__in=["Foyer", "MDE", "La Gommette"])
)
perms = []
for seller in sellers:
favoured_counter = random.choice(counters)
nb_perms = abs(int(random.normalvariate(mu=275, sigma=100)))
active_period_start = self.faker.past_date("-10y")
active_period_end = self.faker.date_between(
active_period_start,
min(now().date(), active_period_start + relativedelta(years=5)),
)
for _ in range(nb_perms):
counter = (
favoured_counter
if random.random() > 0.8
else random.choice(counters)
)
duration = self.faker.time_delta(timedelta(hours=1))
start = make_aware(
self.faker.date_time_between(active_period_start, active_period_end)
)
perms.append(
Permanency(
counter=counter, user=seller, start=start, end=start + duration
)
)
Permanency.objects.bulk_create(perms)

View File

@ -21,7 +21,6 @@
#
#
import os
from django.core.management.base import BaseCommand
@ -37,9 +36,6 @@ class Command(BaseCommand):
)
def handle(self, *args, **options):
root_path = os.path.dirname(
os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
)
files = SithFile.objects.filter(id__in=options["ids"]).all()
for f in files:
f._repair_fs()

View File

@ -13,28 +13,32 @@
#
#
import os
from django.conf import settings
from django.core.management import call_command
from django.core.management.base import BaseCommand
class Command(BaseCommand):
help = "Set up a new instance of the Sith AE"
help = "Set up the development environment."
def handle(self, *args, **options):
root_path = os.path.dirname(
os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
)
try:
os.mkdir(os.path.join(root_path) + "/data")
print("Data dir created")
except Exception as e:
repr(e)
try:
os.remove(os.path.join(root_path, "db.sqlite3"))
print("db.sqlite3 deleted")
except Exception as e:
repr(e)
if not settings.DEBUG:
raise Exception("Never call this command in prod. Never.")
data_dir = settings.BASE_DIR / "data"
settings.EMAIL_BACKEND = "django.core.mail.backends.dummy.EmailBackend"
if not data_dir.is_dir():
data_dir.mkdir()
db_path = settings.BASE_DIR / "db.sqlite3"
if db_path.exists():
call_command("flush", "--noinput")
self.stdout.write("Existing database reset")
call_command("migrate")
self.stdout.write("Add the base fixtures.")
call_command("populate")
self.stdout.write("Generate additional random fixtures")
call_command("populate_more")
self.stdout.write("Build the xapian index")
call_command("rebuild_index", "--noinput")
settings.EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"
self.stdout.write("Setup complete!")

View File

@ -27,6 +27,7 @@ import importlib
import os
import unicodedata
from datetime import date, timedelta
from pathlib import Path
from typing import TYPE_CHECKING, Optional
from django.conf import settings
@ -56,8 +57,6 @@ from django.utils.html import escape
from django.utils.translation import gettext_lazy as _
from phonenumber_field.modelfields import PhoneNumberField
from core import utils
if TYPE_CHECKING:
from club.models import Club
@ -377,7 +376,9 @@ class User(AbstractBaseUser):
USERNAME_FIELD = "username"
def promo_has_logo(self):
return utils.file_exist("./core/static/core/img/promo_%02d.png" % self.promo)
return Path(
settings.BASE_DIR / f"core/static/core/img/promo_{self.promo}.png"
).exists()
def has_module_perms(self, package_name):
return self.is_active

View File

@ -22,7 +22,6 @@
#
#
import os
from collections import OrderedDict
from django.conf import settings
@ -37,7 +36,7 @@ class ScssFinder(FileSystemFinder):
def __init__(self, apps=None, *args, **kwargs):
location = settings.STATIC_ROOT
if not os.path.isdir(location):
if not location.is_dir():
return
self.locations = [("", location)]
self.storages = OrderedDict()

View File

@ -21,61 +21,35 @@
# Place - Suite 330, Boston, MA 02111-1307, USA.
#
#
import os
from urllib.parse import urljoin
import functools
from pathlib import Path
import sass
from django.conf import settings
from django.core.files.base import ContentFile
from django.templatetags.static import static
from django.utils.encoding import force_bytes, iri_to_uri
from django_jinja.builtins.filters import static
from core.scss.storage import ScssFileStorage, find_file
class ScssProcessor(object):
"""If DEBUG mode enabled : compile the scss file
Else : give the path of the corresponding css supposed to already be compiled
Don't forget to use compilestatics to compile scss for production.
"""
@functools.cache
def _scss_storage():
return ScssFileStorage()
prefix = iri_to_uri(getattr(settings, "STATIC_URL", "/static/"))
storage = ScssFileStorage()
scss_extensions = [".scss"]
def __init__(self, path=None):
self.path = path
def _convert_scss(self):
basename, ext = os.path.splitext(self.path)
css_filename = self.path.replace(".scss", ".css")
url = urljoin(self.prefix, css_filename)
if not settings.DEBUG:
return url
if ext not in self.scss_extensions:
return static(self.path)
# Compilation on the fly
def process_scss_path(path: Path):
css_path = path.with_suffix(".css")
if settings.DEBUG:
compile_args = {
"filename": find_file(self.path),
"filename": find_file(path),
"include_paths": settings.SASS_INCLUDE_FOLDERS,
}
if settings.SASS_PRECISION:
compile_args["precision"] = settings.SASS_PRECISION
content = sass.compile(**compile_args)
content = force_bytes(content)
if self.storage.exists(css_filename):
self.storage.delete(css_filename)
self.storage.save(css_filename, ContentFile(content))
return url
def get_converted_scss(self):
if self.path:
return self._convert_scss()
else:
return ""
storage = _scss_storage()
if storage.exists(css_path):
storage.delete(css_path)
storage.save(css_path, ContentFile(content))
return static(css_path)

View File

@ -1,149 +1,149 @@
body{
position: absolute;
width: 100vw;
height: 100vh;
position: absolute;
width: 100vw;
height: 100vh;
padding: 0;
margin: 0;
padding: 0;
margin: 0;
}
#slideshow{
position: relative;
background-color: lightgrey;
position: relative;
background-color: lightgrey;
height: 100%;
*{
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
&:hover{
&::before{
position: absolute;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
z-index: 10;
content: "Click to expand";
color: white;
background-color: rgba(black, 0.5);
}
}
&.fullscreen{
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
background: none;
*{
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
&:hover{
&::before{
position: absolute;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
z-index: 10;
content: "Click to expand";
color: white;
background-color: rgba(black, 0.5);
}
}
&.fullscreen{
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
background: none;
&:before{
display:none;
}
#slides{
height: 100vh;
}
&:before{
display:none;
}
#slides{
position: relative;
height: 100%;
overflow: hidden;
.slide{
position: absolute;
width: 100%;
height: 100%;
display: inline-flex;
justify-content: center;
top: 0px;
background-color: grey;
transition: left 1s ease-out;
img{
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
}
.slide.left{
left: -100%;
}
.slide.center{
left: 0px;
}
.slide.right{
left: 100%;
transition: none;
}
height: 100vh;
}
}
#progress_bullets{
position: absolute;
bottom: 10px;
width: 100%;
height: 10px;
#slides{
position: relative;
height: 100%;
overflow: hidden;
display: flex;
justify-content: center;
.slide{
position: absolute;
width: 100%;
height: 100%;
margin-bottom: 10px;
display: inline-flex;
justify-content: center;
.bullet{
height: 10px;
width: 10px;
top: 0px;
margin-left: 5px;
margin-right: 5px;
background-color: grey;
transition: left 1s ease-out;
border-radius: 50%;
background-color: grey;
&.active{
background-color: #c99836;
}
}
img{
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
}
#progress_bar{
position: absolute;
bottom: 0px;
height: 10px;
background-color: #304c83;
&.init{
width: 0px;
transition: none;
}
&.progress{
width: 100%;
transition: width 10s linear;
}
.slide.left{
left: -100%;
}
.slide.center{
left: 0px;
}
.slide.right{
left: 100%;
transition: none;
}
}
#progress_bullets{
position: absolute;
bottom: 10px;
width: 100%;
height: 10px;
display: flex;
justify-content: center;
margin-bottom: 10px;
.bullet{
height: 10px;
width: 10px;
margin-left: 5px;
margin-right: 5px;
border-radius: 50%;
background-color: grey;
&.active{
background-color: #c99836;
}
}
}
#progress_bar{
position: absolute;
bottom: 0px;
height: 10px;
background-color: #304c83;
&.init{
width: 0px;
transition: none;
}
&.progress{
width: 100%;
transition: width 10s linear;
}
}
}

View File

@ -0,0 +1,30 @@
$first-color: hsl(220, 100%, 50%);
$second-color: hsl(48, 100%, 67%);
$primary-color: hsl(219.9, 53.7%, 50%);
$secondary-color: hsl(204, 64%, 44%);
$primary-color-text: hsl(0, 0%, 100%);
$secondary-color-text: hsla(0, 0%, 0%, 0.87);
$primary-light-color: hsl(219.8, 46.4%, 64.9%);
$primary-dark-color: hsl(203, 75%, 40%);
$secondary-light-color: hsl(40, 68%, 65%);
$secondary-dark-color: hsl(40, 68%, 35%);
$primary-neutral-color: hsl(219.6, 20.8%, 50%);
$primary-neutral-light-color: hsl(0, 0%, 94%);
$primary-neutral-dark-color: hsl(210, 29%, 29%);
$secondary-neutral-color: hsl(204, 64%, 44%);
$secondary-neutral-light-color: hsl(0, 0%, 91%);
$secondary-neutral-dark-color: hsl(40, 57.6%, 17%);
$white-color: hsl(219.6, 20.8%, 98%);
$black-color: hsl(0, 0%, 17%);
$faceblue: hsl(221, 44%, 41%);
$twitblue: hsl(206, 82%, 63%);
$shadow-color: rgb(223, 223, 223);
$background-button-color: hsl(0, 0%, 95%);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1 @@
!function(e){"object"==typeof exports?module.exports=e():"function"==typeof define&&define.amd?define(e):"undefined"!=typeof window?window.JSZipUtils=e():"undefined"!=typeof global?global.JSZipUtils=e():"undefined"!=typeof self&&(self.JSZipUtils=e())}(function(){return function o(i,f,u){function s(n,e){if(!f[n]){if(!i[n]){var t="function"==typeof require&&require;if(!e&&t)return t(n,!0);if(a)return a(n,!0);throw new Error("Cannot find module '"+n+"'")}var r=f[n]={exports:{}};i[n][0].call(r.exports,function(e){var t=i[n][1][e];return s(t||e)},r,r.exports,o,i,f,u)}return f[n].exports}for(var a="function"==typeof require&&require,e=0;e<u.length;e++)s(u[e]);return s}({1:[function(e,t,n){"use strict";var u={};function r(){try{return new window.XMLHttpRequest}catch(e){}}u._getBinaryFromXHR=function(e){return e.response||e.responseText};var s="undefined"!=typeof window&&window.ActiveXObject?function(){return r()||function(){try{return new window.ActiveXObject("Microsoft.XMLHTTP")}catch(e){}}()}:r;u.getBinaryContent=function(t,n){var e,r,o,i;"function"==typeof(n=n||{})?(i=n,n={}):"function"==typeof n.callback&&(i=n.callback),i||"undefined"==typeof Promise?(r=function(e){i(null,e)},o=function(e){i(e,null)}):e=new Promise(function(e,t){r=e,o=t});try{var f=s();f.open("GET",t,!0),"responseType"in f&&(f.responseType="arraybuffer"),f.overrideMimeType&&f.overrideMimeType("text/plain; charset=x-user-defined"),f.onreadystatechange=function(e){if(4===f.readyState)if(200===f.status||0===f.status)try{r(u._getBinaryFromXHR(f))}catch(e){o(new Error(e))}else o(new Error("Ajax error for "+t+" : "+this.status+" "+this.statusText))},n.progress&&(f.onprogress=function(e){n.progress({path:t,originalEvent:e,percent:e.loaded/e.total*100,loaded:e.loaded,total:e.total})}),f.send()}catch(e){o(new Error(e),null)}return e},t.exports=u},{}]},{},[1])(1)});

13
core/static/core/js/jszip/jszip.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Jimmy Wärting
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1 @@
export*from"./src/es6.js";

View File

@ -0,0 +1 @@
import FileSystemHandle from"./FileSystemHandle.js";import{errors}from"./util.js";const{GONE:GONE,MOD_ERR:MOD_ERR}=errors,kAdapter=Symbol("adapter");class FileSystemDirectoryHandle extends FileSystemHandle{[kAdapter];constructor(e){super(e),this[kAdapter]=e}async getDirectoryHandle(e,t={}){if(""===e)throw new TypeError("Name can't be an empty string.");if("."===e||".."===e||e.includes("/"))throw new TypeError("Name contains invalid characters.");t.create=!!t.create;const r=await this[kAdapter].getDirectoryHandle(e,t);return new FileSystemDirectoryHandle(r)}async*entries(){const{FileSystemFileHandle:e}=await import("./FileSystemFileHandle.js");for await(const[t,r]of this[kAdapter].entries())yield[r.name,"file"===r.kind?new e(r):new FileSystemDirectoryHandle(r)]}async*getEntries(){const{FileSystemFileHandle:e}=await import("./FileSystemFileHandle.js");console.warn("deprecated, use .entries() instead");for await(let t of this[kAdapter].entries())yield"file"===t.kind?new e(t):new FileSystemDirectoryHandle(t)}async getFileHandle(e,t={}){const{FileSystemFileHandle:r}=await import("./FileSystemFileHandle.js");if(""===e)throw new TypeError("Name can't be an empty string.");if("."===e||".."===e||e.includes("/"))throw new TypeError("Name contains invalid characters.");t.create=!!t.create;return new r(await this[kAdapter].getFileHandle(e,t))}async removeEntry(e,t={}){if(""===e)throw new TypeError("Name can't be an empty string.");if("."===e||".."===e||e.includes("/"))throw new TypeError("Name contains invalid characters.");return t.recursive=!!t.recursive,this[kAdapter].removeEntry(e,t)}async resolve(e){if(await e.isSameEntry(this))return[];const t=[{handle:this,path:[]}];for(;t.length;){let{handle:r,path:n}=t.pop();for await(const a of r.values()){if(await a.isSameEntry(e))return[...n,a.name];"directory"===a.kind&&t.push({handle:a,path:[...n,a.name]})}}return null}async*keys(){for await(const[e]of this[kAdapter].entries())yield e}async*values(){for await(const[e,t]of this)yield t}[Symbol.asyncIterator](){return this.entries()}}if(Object.defineProperty(FileSystemDirectoryHandle.prototype,Symbol.toStringTag,{value:"FileSystemDirectoryHandle",writable:!1,enumerable:!1,configurable:!0}),Object.defineProperties(FileSystemDirectoryHandle.prototype,{getDirectoryHandle:{enumerable:!0},entries:{enumerable:!0},getFileHandle:{enumerable:!0},removeEntry:{enumerable:!0}}),globalThis.FileSystemDirectoryHandle){const e=globalThis.FileSystemDirectoryHandle.prototype;async function ensureDoActuallyStillExist(e){const t=await navigator.storage.getDirectory();if(null===await t.resolve(e))throw new DOMException(...GONE)}e.resolve=async function(e){if(await e.isSameEntry(this))return[];const t=[{handle:this,path:[]}];for(;t.length;){let{handle:r,path:n}=t.pop();for await(const a of r.values()){if(await a.isSameEntry(e))return[...n,a.name];"directory"===a.kind&&t.push({handle:a,path:[...n,a.name]})}}return null};const t=e.entries;e.entries=async function*(){await ensureDoActuallyStillExist(this),yield*t.call(this)},e[Symbol.asyncIterator]=async function*(){yield*this.entries()};const r=e.removeEntry;e.removeEntry=async function(e,n={}){return r.call(this,e,n).catch((async e=>{if(e instanceof DOMException&&"UnknownError"===e.name&&!n.recursive){if(!(await t.call(this).next()).done)throw new DOMException(...MOD_ERR)}throw e}))}}export default FileSystemDirectoryHandle;export{FileSystemDirectoryHandle};

View File

@ -0,0 +1 @@
import FileSystemHandle from"./FileSystemHandle.js";import FileSystemWritableFileStream from"./FileSystemWritableFileStream.js";import{errors}from"./util.js";const{INVALID:INVALID,SYNTAX:SYNTAX,GONE:GONE}=errors,kAdapter=Symbol("adapter");class FileSystemFileHandle extends FileSystemHandle{[kAdapter];constructor(e){super(e),this[kAdapter]=e}async createWritable(e={}){return new FileSystemWritableFileStream(await this[kAdapter].createWritable(e))}async getFile(){return this[kAdapter].getFile()}}if(Object.defineProperty(FileSystemFileHandle.prototype,Symbol.toStringTag,{value:"FileSystemFileHandle",writable:!1,enumerable:!1,configurable:!0}),Object.defineProperties(FileSystemFileHandle.prototype,{createWritable:{enumerable:!0},getFile:{enumerable:!0}}),globalThis.FileSystemFileHandle&&!globalThis.FileSystemFileHandle.prototype.createWritable){const e=new WeakMap;let t;const a=()=>{let e,t;onmessage=async a=>{const i=a.ports[0],r=a.data;switch(r.type){case"open":const a=r.name;let i=await navigator.storage.getDirectory();for(const e of r.path)i=await i.getDirectoryHandle(e);e=await i.getFileHandle(a),t=await e.createSyncAccessHandle();break;case"write":t.write(r.data,{at:r.position}),t.flush();break;case"truncate":t.truncate(r.size);break;case"abort":case"close":t.close()}i.postMessage(0)}};globalThis.FileSystemFileHandle.prototype.createWritable=async function(i){if(!t){const e=`(${a.toString()})()`,i=new Blob([e],{type:"text/javascript"});t=URL.createObjectURL(i)}const r=new Worker(t,{type:"module"});let n=0;const s=new TextEncoder;let o=await this.getFile().then((e=>e.size));const l=e=>new Promise(((t,a)=>{const i=new MessageChannel;i.port1.onmessage=e=>{e.data instanceof Error?a(e.data):t(e.data),i.port1.close(),i.port2.close(),i.port1.onmessage=null},r.postMessage(e,[i.port2])})),c=await navigator.storage.getDirectory(),p=await e.get(this),y=await c.resolve(p);if(null===y)throw new DOMException(...GONE);let d;await l({type:"open",path:y,name:this.name}),!1===i?.keepExistingData&&(await l({type:"truncate",size:0}),o=0);return new FileSystemWritableFileStream({start:e=>{d=e},async write(e){if("write"===(e=e?.constructor===Object?{...e}:{type:"write",data:e,position:n}).type){if(!("data"in e))throw await l({type:"close"}),new DOMException(...SYNTAX("write requires a data argument"));if(e.position??=n,"string"==typeof e.data)e.data=s.encode(e.data);else if(e.data instanceof ArrayBuffer)e.data=new Uint8Array(e.data);else if(e.data instanceof Uint8Array||!ArrayBuffer.isView(e.data)){if(!(e.data instanceof Uint8Array)){const t=await new Response(e.data).arrayBuffer();e.data=new Uint8Array(t)}}else e.data=new Uint8Array(e.data.buffer,e.data.byteOffset,e.data.byteLength);Number.isInteger(e.position)&&e.position>=0&&(n=e.position),n+=e.data.byteLength,o+=e.data.byteLength}else{if("seek"===e.type){if(Number.isInteger(e.position)&&e.position>=0){if(o<e.position)throw new DOMException(...INVALID);return console.log("seeking",e),void(n=e.position)}throw await l({type:"close"}),new DOMException(...SYNTAX("seek requires a position argument"))}if("truncate"===e.type){if(!(Number.isInteger(e.size)&&e.size>=0))throw await l({type:"close"}),new DOMException(...SYNTAX("truncate requires a size argument"));o=e.size,n>o&&(n=o)}}await l(e)},async close(){await l({type:"close"}),r.terminate()},async abort(e){await l({type:"abort",reason:e}),r.terminate()}})};const i=FileSystemDirectoryHandle.prototype.getFileHandle;FileSystemDirectoryHandle.prototype.getFileHandle=async function(...t){const a=await i.call(this,...t);return e.set(a,this),a}}export default FileSystemFileHandle;export{FileSystemFileHandle};

View File

@ -0,0 +1 @@
const kAdapter=Symbol("adapter");class FileSystemHandle{[kAdapter];name;kind;constructor(e){this.kind=e.kind,this.name=e.name,this[kAdapter]=e}async queryPermission(e={}){const{mode:r="read"}=e,t=this[kAdapter];if(t.queryPermission)return t.queryPermission({mode:r});if("read"===r)return"granted";if("readwrite"===r)return t.writable?"granted":"denied";throw new TypeError(`Mode ${r} must be 'read' or 'readwrite'`)}async requestPermission({mode:e="read"}={}){const r=this[kAdapter];if(r.requestPermission)return r.requestPermission({mode:e});if("read"===e)return"granted";if("readwrite"===e)return r.writable?"granted":"denied";throw new TypeError(`Mode ${e} must be 'read' or 'readwrite'`)}async remove(e={}){await this[kAdapter].remove(e)}async isSameEntry(e){return this===e||!(!e||"object"!=typeof e||this.kind!==e.kind||!e[kAdapter])&&this[kAdapter].isSameEntry(e[kAdapter])}}Object.defineProperty(FileSystemHandle.prototype,Symbol.toStringTag,{value:"FileSystemHandle",writable:!1,enumerable:!1,configurable:!0}),globalThis.FileSystemHandle&&(globalThis.FileSystemHandle.prototype.queryPermission??=function(e){return"granted"});export default FileSystemHandle;export{FileSystemHandle};

View File

@ -0,0 +1 @@
import config from"./config.js";const{WritableStream:WritableStream}=config;class FileSystemWritableFileStream extends WritableStream{#e;constructor(e){super(e),this.#e=e,Object.setPrototypeOf(this,FileSystemWritableFileStream.prototype),this._closed=!1}async close(){this._closed=!0;const e=this.getWriter(),t=e.close();return e.releaseLock(),t}seek(e){return this.write({type:"seek",position:e})}truncate(e){return this.write({type:"truncate",size:e})}write(e){if(this._closed)return Promise.reject(new TypeError("Cannot write to a CLOSED writable stream"));const t=this.getWriter(),r=t.write(e);return t.releaseLock(),r}}Object.defineProperty(FileSystemWritableFileStream.prototype,Symbol.toStringTag,{value:"FileSystemWritableFileStream",writable:!1,enumerable:!1,configurable:!0}),Object.defineProperties(FileSystemWritableFileStream.prototype,{close:{enumerable:!0},seek:{enumerable:!0},truncate:{enumerable:!0},write:{enumerable:!0}}),!globalThis.FileSystemFileHandle||globalThis.FileSystemFileHandle.prototype.createWritable||globalThis.FileSystemWritableFileStream||(globalThis.FileSystemWritableFileStream=FileSystemWritableFileStream);export default FileSystemWritableFileStream;export{FileSystemWritableFileStream};

View File

@ -0,0 +1 @@
import{errors}from"../util.js";const{INVALID:INVALID,GONE:GONE,MISMATCH:MISMATCH,MOD_ERR:MOD_ERR,SYNTAX:SYNTAX,SECURITY:SECURITY,DISALLOWED:DISALLOWED}=errors;export class Sink{constructor(){}write(e){}close(){}}export class FileHandle{constructor(){this._path=""}async getFile(){return new File([],"")}async createWritable(){}async isSameEntry(e){return e._path===this._path}}export class FolderHandle{constructor(){this._path=""}async*entries(){yield}async isSameEntry(e){return e._path===this._path}async getDirectoryHandle(e,r){return new FolderHandle}async getFileHandle(e,r){return new FileHandle}async removeEntry(e,r){}}const fs=new FolderHandle("");export default()=>fs;

View File

@ -0,0 +1 @@
import{errors}from"../util.js";const{INVALID:INVALID,GONE:GONE,MISMATCH:MISMATCH,MOD_ERR:MOD_ERR,SYNTAX:SYNTAX}=errors,DIR={headers:{"content-type":"dir"}},FILE=()=>({headers:{"content-type":"file","last-modified":Date.now()}}),hasOwn=Object.prototype.hasOwnProperty;class Sink{constructor(e,t,i){this._cache=e,this.path=t,this.size=i.size,this.position=0,this.file=i}write(e,t){if("object"==typeof e)if("write"===e.type){if(Number.isInteger(e.position)&&e.position>=0&&(this.size<e.position&&(this.file=new Blob([this.file,new ArrayBuffer(e.position-this.size)])),this.position=e.position),!("data"in e))throw new DOMException(...SYNTAX("write requires a data argument"));e=e.data}else{if("seek"===e.type){if(Number.isInteger(e.position)&&e.position>=0){if(this.size<e.position)throw new DOMException(...INVALID);return void(this.position=e.position)}throw new DOMException(...SYNTAX("seek requires a position argument"))}if("truncate"===e.type){if(Number.isInteger(e.size)&&e.size>=0){let t=this.file;return t=e.size<this.size?t.slice(0,e.size):new File([t,new Uint8Array(e.size-this.size)],t.name),this.size=t.size,this.position>t.size&&(this.position=t.size),void(this.file=t)}throw new DOMException(...SYNTAX("truncate requires a size argument"))}}e=new Blob([e]);let i=this.file;const s=i.slice(0,this.position),n=i.slice(this.position+e.size);let a=this.position-s.size;a<0&&(a=0),i=new File([s,new Uint8Array(a),e,n],i.name),this.size=i.size,this.position+=e.size,this.file=i}async close(){const[e]=await this._cache.keys(this.path);if(!e)throw new DOMException(...GONE);return this._cache.put(this.path,new Response(this.file,FILE()))}}export class FileHandle{constructor(e,t){this._cache=t,this.path=e,this.kind="file",this.writable=!0,this.readable=!0}get name(){return this.path.split("/").pop()}async isSameEntry(e){return this.path===e.path}async getFile(){const e=await this._cache.match(this.path);if(!e)throw new DOMException(...GONE);const t=await e.blob();return new File([t],this.name,{lastModified:+e.headers.get("last-modified")})}async createWritable(e){const[t]=await this._cache.keys(this.path);if(!t)throw new DOMException(...GONE);return new Sink(this._cache,this.path,e.keepExistingData?await this.getFile():new File([],this.name))}}export class FolderHandle{constructor(e,t){this._dir=e,this.writable=!0,this.readable=!0,this._cache=t,this.kind="directory",this.name=e.split("/").pop()}async*entries(){for(const[e,t]of Object.entries(await this._tree))yield[e.split("/").pop(),t?new FileHandle(e,this._cache):new FolderHandle(e,this._cache)]}async isSameEntry(e){return this._dir===e._dir}async getDirectoryHandle(e,t){const i=this._dir.endsWith("/")?this._dir+e:`${this._dir}/${e}`,s=await this._tree;if(hasOwn.call(s,i)){if(s[i])throw new DOMException(...MISMATCH);return new FolderHandle(i,this._cache)}if(t.create)return s[i]=!1,await this._cache.put(i,new Response("{}",DIR)),await this._save(s),new FolderHandle(i,this._cache);throw new DOMException(...GONE)}get _tree(){return this._cache.match(this._dir).then((e=>e.json())).catch((e=>{throw new DOMException(...GONE)}))}_save(e){return this._cache.put(this._dir,new Response(JSON.stringify(e),DIR))}async getFileHandle(e,t){const i=this._dir.endsWith("/")?this._dir+e:`${this._dir}/${e}`,s=await this._tree;if(hasOwn.call(s,i)){if(!s[i])throw new DOMException(...MISMATCH);return new FileHandle(i,this._cache)}if(t.create){const e=await this._tree;return e[i]=!0,await this._cache.put(i,new Response("",FILE())),await this._save(e),new FileHandle(i,this._cache)}throw new DOMException(...GONE)}async removeEntry(e,t){const i=await this._tree,s=this._dir.endsWith("/")?this._dir+e:`${this._dir}/${e}`;if(!hasOwn.call(i,s))throw new DOMException(...GONE);if(t.recursive){const e=[...Object.entries(i)];for(;e.length;){const[t,i]=e.pop();if(i)await this._cache.delete(t);else{const i=await this._cache.match(t).then((e=>e.json()));e.push(...Object.entries(i))}}delete i[s]}else{const e=i[s];if(delete i[s],e)await this._cache.delete(s);else{const e=await this._cache.match(s).then((e=>e.json()));if(Object.keys(e).length)throw new DOMException(...MOD_ERR);await this._cache.delete(s)}}await this._save(i)}}export default async function(){const e=await caches.open("sandboxed-fs");return await e.match("/")||await e.put("/",new Response("{}",DIR)),new FolderHandle(location.origin+"/",e)}

View File

@ -0,0 +1 @@
import{join,basename}from"https://deno.land/std@0.108.0/path/mod.ts";import{errors}from"../util.js";const{INVALID:INVALID,GONE:GONE,MISMATCH:MISMATCH,MOD_ERR:MOD_ERR,SYNTAX:SYNTAX}=errors;async function fileFrom(t){const e=Deno.readFileSync(t),i=await Deno.stat(t);return new File([e],basename(t),{lastModified:Number(i.mtime)})}export class Sink{constructor(t,e){this.fileHandle=t,this.size=e,this.position=0}async abort(){await this.fileHandle.close()}async write(t){if("object"==typeof t)if("write"===t.type){if(Number.isInteger(t.position)&&t.position>=0&&(this.position=t.position),!("data"in t))throw await this.fileHandle.close(),new DOMException(...SYNTAX("write requires a data argument"));t=t.data}else{if("seek"===t.type){if(Number.isInteger(t.position)&&t.position>=0){if(this.size<t.position)throw new DOMException(...INVALID);return void(this.position=t.position)}throw await this.fileHandle.close(),new DOMException(...SYNTAX("seek requires a position argument"))}if("truncate"===t.type){if(Number.isInteger(t.size)&&t.size>=0)return await this.fileHandle.truncate(t.size),this.size=t.size,void(this.position>this.size&&(this.position=this.size));throw await this.fileHandle.close(),new DOMException(...SYNTAX("truncate requires a size argument"))}}if(t instanceof ArrayBuffer)t=new Uint8Array(t);else if("string"==typeof t)t=(new TextEncoder).encode(t);else if(t instanceof Blob){await this.fileHandle.seek(this.position,Deno.SeekMode.Start);for await(const e of t.stream()){const t=await this.fileHandle.write(e);this.position+=t,this.size+=t}return}await this.fileHandle.seek(this.position,Deno.SeekMode.Start);const e=await this.fileHandle.write(t);this.position+=e,this.size+=e}async close(){await this.fileHandle.close()}}export class FileHandle{#t;constructor(t,e){this.#t=t,this.name=e,this.kind="file"}async getFile(){return await Deno.stat(this.#t).catch((t=>{if("NotFound"===t.name)throw new DOMException(...GONE)})),fileFrom(this.#t)}async isSameEntry(t){return this.#t===this.#e.apply(t)}#e(){return this.#t}async createWritable(t){const e=await Deno.open(this.#t,{write:!0,truncate:!t.keepExistingData}).catch((t=>{if("NotFound"===t.name)throw new DOMException(...GONE);throw t})),{size:i}=await e.stat();return new Sink(e,i)}}export class FolderHandle{#t="";constructor(t,e=""){this.name=e,this.kind="directory",this.#t=join(t)}async isSameEntry(t){return this.#t===this.#e.apply(t)}#e(){return this.#t}async*entries(){const t=this.#t;try{for await(const e of Deno.readDir(t)){const{name:i}=e,n=join(t,i),o=await Deno.lstat(n);o.isFile?yield[i,new FileHandle(n,i)]:o.isDirectory&&(yield[i,new FolderHandle(n,i)])}}catch(t){throw"NotFound"===t.name?new DOMException(...GONE):t}}async getDirectoryHandle(t,e){const i=join(this.#t,t),n=await Deno.lstat(i).catch((t=>{if("NotFound"!==t.name)throw t})),o=n?.isDirectory;if(n&&o)return new FolderHandle(i,t);if(n&&!o)throw new DOMException(...MISMATCH);if(!e.create)throw new DOMException(...GONE);return await Deno.mkdir(i),new FolderHandle(i,t)}async getFileHandle(t,e){const i=join(this.#t,t),n=await Deno.lstat(i).catch((t=>{if("NotFound"!==t.name)throw t})),o=n?.isFile;if(n&&o)return new FileHandle(i,t);if(n&&!o)throw new DOMException(...MISMATCH);if(!e.create)throw new DOMException(...GONE);return(await Deno.open(i,{create:!0,write:!0})).close(),new FileHandle(i,t)}async queryPermission(){return"granted"}async removeEntry(t,e){const i=join(this.#t,t);(await Deno.lstat(i).catch((t=>{if("NotFound"===t.name)throw new DOMException(...GONE);throw t}))).isDirectory?e.recursive?await Deno.remove(i,{recursive:!0}).catch((t=>{if("ENOTEMPTY"===t.code)throw new DOMException(...MOD_ERR);throw t})):await Deno.remove(i).catch((()=>{throw new DOMException(...MOD_ERR)})):await Deno.remove(i)}}export default t=>new FolderHandle(join(Deno.cwd(),t));

View File

@ -0,0 +1 @@
import{errors}from"../util.js";import config from"../config.js";const{WritableStream:WritableStream,TransformStream:TransformStream,DOMException:DOMException,Blob:Blob}=config,{GONE:GONE}=errors,isOldSafari=/constructor/i.test(window.HTMLElement);export class FileHandle{constructor(e="unkown"){this.name=e,this.kind="file"}async getFile(){throw new DOMException(...GONE)}async isSameEntry(e){return this===e}async createWritable(e={}){const t=await(navigator.serviceWorker?.getRegistration()),r=document.createElement("a"),s=new TransformStream,a=s.writable;if(r.download=this.name,isOldSafari||!t){let e=[];s.readable.pipeTo(new WritableStream({write(t){e.push(new Blob([t]))},close(){const t=new Blob(e,{type:"application/octet-stream; charset=utf-8"});e=[],r.href=URL.createObjectURL(t),r.click(),setTimeout((()=>URL.revokeObjectURL(r.href)),1e4)}}))}else{const{writable:r,readablePort:a}=new RemoteWritableStream(WritableStream),o=encodeURIComponent(this.name).replace(/['()]/g,escape).replace(/\*/g,"%2A"),n={"content-disposition":"attachment; filename*=UTF-8''"+o,"content-type":"application/octet-stream; charset=utf-8",...e.size?{"content-length":e.size}:{}},i=setTimeout((()=>t.active.postMessage(0)),1e4);s.readable.pipeThrough(new TransformStream({transform(e,t){if(e instanceof Uint8Array)return t.enqueue(e);const r=new Response(e).body.getReader(),s=e=>r.read().then((e=>e.done?0:s(t.enqueue(e.value))));return s()}})).pipeTo(r).finally((()=>{clearInterval(i)})),t.active.postMessage({url:t.scope+o,headers:n,readablePort:a},[a]);const c=document.createElement("iframe");c.hidden=!0,c.src=t.scope+o,document.body.appendChild(c)}return a.getWriter()}}const WRITE=0,PULL=0,ERROR=1,ABORT=1,CLOSE=2;class MessagePortSink{constructor(e){e.onmessage=e=>this._onMessage(e.data),this._port=e,this._resetReady()}start(e){return this._controller=e,this._readyPromise}write(e){const t={type:0,chunk:e};return this._port.postMessage(t,[e.buffer]),this._resetReady(),this._readyPromise}close(){this._port.postMessage({type:2}),this._port.close()}abort(e){this._port.postMessage({type:1,reason:e}),this._port.close()}_onMessage(e){0===e.type&&this._resolveReady(),1===e.type&&this._onError(e.reason)}_onError(e){this._controller.error(e),this._rejectReady(e),this._port.close()}_resetReady(){this._readyPromise=new Promise(((e,t)=>{this._readyResolve=e,this._readyReject=t})),this._readyPending=!0}_resolveReady(){this._readyResolve(),this._readyPending=!1}_rejectReady(e){this._readyPending||this._resetReady(),this._readyPromise.catch((()=>{})),this._readyReject(e),this._readyPending=!1}}class RemoteWritableStream{constructor(e){const t=new MessageChannel;this.readablePort=t.port1,this.writable=new e(new MessagePortSink(t.port2))}}

View File

@ -0,0 +1 @@
import{errors}from"../util.js";const{INVALID:INVALID,GONE:GONE,MISMATCH:MISMATCH,MOD_ERR:MOD_ERR,SYNTAX:SYNTAX,ABORT:ABORT}=errors;function setupTxErrorHandler(e,t){e.onerror=()=>t(e.error),e.onabort=()=>t(e.error||new DOMException(...ABORT))}class Sink{constructor(e,t,i,s){this.db=e,this.id=t,this.size=i,this.position=0,this.file=s}write(e){if("object"==typeof e)if("write"===e.type){if(Number.isInteger(e.position)&&e.position>=0&&(this.size<e.position&&(this.file=new File([this.file,new ArrayBuffer(e.position-this.size)],this.file.name,this.file)),this.position=e.position),!("data"in e))throw new DOMException(...SYNTAX("write requires a data argument"));e=e.data}else{if("seek"===e.type){if(Number.isInteger(e.position)&&e.position>=0){if(this.size<e.position)throw new DOMException(...INVALID);return void(this.position=e.position)}throw new DOMException(...SYNTAX("seek requires a position argument"))}if("truncate"===e.type){if(Number.isInteger(e.size)&&e.size>=0){let t=this.file;return t=e.size<this.size?new File([t.slice(0,e.size)],t.name,t):new File([t,new Uint8Array(e.size-this.size)],t.name,t),this.size=t.size,this.position>t.size&&(this.position=t.size),void(this.file=t)}throw new DOMException(...SYNTAX("truncate requires a size argument"))}}e=new Blob([e]);let t=this.file;const i=t.slice(0,this.position),s=t.slice(this.position+e.size);let n=this.position-i.size;n<0&&(n=0),t=new File([i,new Uint8Array(n),e,s],t.name),this.size=t.size,this.position+=e.size,this.file=t}close(){return new Promise(((e,t)=>{const[i,s]=store(this.db);s.get(this.id).onsuccess=e=>{e.target.result?s.put(this.file,this.id):t(new DOMException(...GONE))},i.oncomplete=()=>e(),i.onerror=t,i.onabort=t}))}}class FileHandle{constructor(e,t,i){this._db=e,this._id=t,this.name=i,this.kind="file",this.readable=!0,this.writable=!0}async isSameEntry(e){return this._id===e._id}async getFile(){const e=await new Promise(((e,t)=>{const i=store(this._db)[1].get(this._id);i.onsuccess=t=>e(t.target.result),i.onerror=e=>t(e.target.error)}));if(!e)throw new DOMException(...GONE);return e}async createWritable(e){let t=await this.getFile();return t=e.keepExistingData?t:new File([],this.name),new Sink(this._db,this._id,t.size,t)}}function store(e){const t=e.transaction("entries","readwrite",{durability:"relaxed"});return[t,t.objectStore("entries")]}function rimraf(e,t,i=!0){const{source:s,result:n}=e.target;for(const[e,r]of Object.values(t||n))r?s.delete(e):i?(s.get(e).onsuccess=rimraf,s.delete(e)):s.get(e).onsuccess=t=>{0!==Object.keys(t.target.result).length?t.target.transaction.abort():s.delete(e)}}class FolderHandle{constructor(e,t,i){this._db=e,this._id=t,this.kind="directory",this.name=i,this.readable=!0,this.writable=!0}async*entries(){const e=store(this._db)[1].get(this._id);await new Promise(((t,i)=>{e.onsuccess=()=>t(),e.onerror=()=>i(e.error)}));const t=e.result;if(!t)throw new DOMException(...GONE);for(const[e,[i,s]]of Object.entries(t))yield[e,s?new FileHandle(this._db,i,e):new FolderHandle(this._db,i,e)]}isSameEntry(e){return this._id===e._id}getDirectoryHandle(e,t){return new Promise(((i,s)=>{const n=store(this._db)[1],r=n.get(this._id);r.onsuccess=()=>{const o=r.result,c=o[e];c?c[1]?s(new DOMException(...MISMATCH)):i(new FolderHandle(this._db,c[0],e)):t.create?n.add({}).onsuccess=t=>{const s=t.target.result;o[e]=[s,!1],n.put(o,this._id).onsuccess=()=>i(new FolderHandle(this._db,s,e))}:s(new DOMException(...GONE))}}))}getFileHandle(e,t){return new Promise(((i,s)=>{const n=store(this._db)[1],r=n.get(this._id);r.onsuccess=()=>{const o=r.result,c=o[e];if(c&&c[1]&&i(new FileHandle(this._db,c[0],e)),c&&!c[1]&&s(new DOMException(...MISMATCH)),c||t.create||s(new DOMException(...GONE)),!c&&t.create){const t=n.put(new File([],e));t.onsuccess=()=>{const s=t.result;o[e]=[s,!0];n.put(o,this._id).onsuccess=()=>{i(new FileHandle(this._db,s,e))}}}}}))}async removeEntry(e,t){return new Promise(((i,s)=>{const[n,r]=store(this._db),o=r.get(this._id);o.onsuccess=i=>{const n=o.result,c={_:n[e]};if(!c._)return s(new DOMException(...GONE));delete n[e],r.put(n,this._id),rimraf(i,c,!!t.recursive)},n.oncomplete=i,n.onerror=s,n.onabort=()=>{s(new DOMException(...MOD_ERR))}}))}}export default(e={persistent:!1})=>new Promise((e=>{const t=indexedDB.open("fileSystem");t.onupgradeneeded=()=>{const e=t.result;e.createObjectStore("entries",{autoIncrement:!0}).transaction.oncomplete=t=>{e.transaction("entries","readwrite").objectStore("entries").add({})}},t.onsuccess=()=>{e(new FolderHandle(t.result,1,""))}}));

View File

@ -0,0 +1 @@
import{errors}from"../util.js";const{GONE:GONE,MISMATCH:MISMATCH,SYNTAX:SYNTAX,DISALLOWED:DISALLOWED}=errors;export class FileHandle{constructor(e,t){this.name=e.name,this.kind="file",this._deleted=!1,this._root=t,this._entry=e,this.writable=!1,this.readable=!0}async getFile(){const e=await fetch(`https://cdn.jsdelivr.net/${this._root}/${this.name}`),t=await e.blob();return new File([t],this.name,{type:t.type,lastModified:this._entry.time})}async createWritable(){throw new DOMException(...DISALLOWED)}async isSameEntry(e){return this===e}}function toDic(e,t){const n={};for(const i of e)i.time=+new Date(i.time),"file"===i.type?n[i.name]=new FileHandle(i,t):n[i.name]=new FolderHandle(i.files,`${t}/${i.name}`,i.name);return n}export class FolderHandle{constructor(e,t,n=""){this.name=n,this.kind="directory",this._deleted=!1,this._entries=toDic(e,t),this.writable=!1,this.readable=!0}async*entries(){yield*Object.entries(this._entries)}async isSameEntry(e){return this===e}async getDirectoryHandle(e,t){if(this._deleted)throw new DOMException(...GONE);const n=this._entries[e];if(n){if(n instanceof FileHandle)throw new DOMException(...MISMATCH);return n}throw t.create?new DOMException(...DISALLOWED):new DOMException(...GONE)}async getFileHandle(e,t){const n=this._entries[e],i=n instanceof FileHandle;if(n&&i)return n;if(n&&!i)throw new DOMException(...MISMATCH);if(!n&&!t.create)throw new DOMException(...GONE);if(!n&&t.create)throw new DOMException(...DISALLOWED)}async removeEntry(e,t){throw new DOMException(...DISALLOWED)}}export default async e=>{const t=await fetch(`https://data.jsdelivr.com/v1/package/${e}`),{files:n}=await t.json();return new FolderHandle(n,e)};

View File

@ -0,0 +1 @@
import{errors}from"../util.js";import config from"../config.js";const{File:File,Blob:Blob,DOMException:DOMException}=config,{INVALID:INVALID,GONE:GONE,MISMATCH:MISMATCH,MOD_ERR:MOD_ERR,SYNTAX:SYNTAX,SECURITY:SECURITY,DISALLOWED:DISALLOWED}=errors;export class Sink{constructor(e,i){this.fileHandle=e,this.file=i,this.size=i.size,this.position=0}write(e){let i=this.file;if("object"==typeof e)if("write"===e.type){if(Number.isInteger(e.position)&&e.position>=0&&(this.position=e.position,this.size<e.position&&(this.file=new File([this.file,new ArrayBuffer(e.position-this.size)],this.file.name,this.file))),!("data"in e))throw new DOMException(...SYNTAX("write requires a data argument"));e=e.data}else{if("seek"===e.type){if(Number.isInteger(e.position)&&e.position>=0){if(this.size<e.position)throw new DOMException(...INVALID);return void(this.position=e.position)}throw new DOMException(...SYNTAX("seek requires a position argument"))}if("truncate"===e.type){if(Number.isInteger(e.size)&&e.size>=0)return i=e.size<this.size?new File([i.slice(0,e.size)],i.name,i):new File([i,new Uint8Array(e.size-this.size)],i.name),this.size=i.size,this.position>i.size&&(this.position=i.size),void(this.file=i);throw new DOMException(...SYNTAX("truncate requires a size argument"))}}e=new Blob([e]);let t=this.file;const s=t.slice(0,this.position),n=t.slice(this.position+e.size);let o=this.position-s.size;o<0&&(o=0),t=new File([s,new Uint8Array(o),e,n],t.name),this.size=t.size,this.position+=e.size,this.file=t}close(){if(this.fileHandle._deleted)throw new DOMException(...GONE);this.fileHandle._file=this.file,this.file=this.position=this.size=null,this.fileHandle.onclose&&this.fileHandle.onclose(this.fileHandle)}}export class FileHandle{constructor(e="",i=new File([],e),t=!0){this._file=i,this.name=e,this.kind="file",this._deleted=!1,this.writable=t,this.readable=!0}async getFile(){if(this._deleted)throw new DOMException(...GONE);return this._file}async createWritable(e){if(!this.writable)throw new DOMException(...DISALLOWED);if(this._deleted)throw new DOMException(...GONE);const i=e.keepExistingData?await this.getFile():new File([],this.name);return new Sink(this,i)}async isSameEntry(e){return this===e}async _destroy(){this._deleted=!0,this._file=null}}export class FolderHandle{constructor(e,i=!0){this.name=e,this.kind="directory",this._deleted=!1,this._entries={},this.writable=i,this.readable=!0}async*entries(){if(this._deleted)throw new DOMException(...GONE);yield*Object.entries(this._entries)}async isSameEntry(e){return this===e}async getDirectoryHandle(e,i){if(this._deleted)throw new DOMException(...GONE);const t=this._entries[e];if(t){if(t instanceof FileHandle)throw new DOMException(...MISMATCH);return t}if(i.create)return this._entries[e]=new FolderHandle(e);throw new DOMException(...GONE)}async getFileHandle(e,i){const t=this._entries[e],s=t instanceof FileHandle;if(t&&s)return t;if(t&&!s)throw new DOMException(...MISMATCH);if(!t&&!i.create)throw new DOMException(...GONE);return!t&&i.create?this._entries[e]=new FileHandle(e):void 0}async removeEntry(e,i){const t=this._entries[e];if(!t)throw new DOMException(...GONE);await t._destroy(i.recursive),delete this._entries[e]}async _destroy(e){for(let i of Object.values(this._entries)){if(!e)throw new DOMException(...MOD_ERR);await i._destroy(e)}this._entries={},this._deleted=!0}}const fs=new FolderHandle("");export default()=>fs;

View File

@ -0,0 +1 @@
import fs from"node:fs/promises";import{join}from"node:path";import{errors}from"../util.js";import config from"../config.js";const{DOMException:DOMException}=config,{INVALID:INVALID,GONE:GONE,MISMATCH:MISMATCH,MOD_ERR:MOD_ERR,SYNTAX:SYNTAX}=errors;function isBlob(t){return t&&"object"==typeof t&&"function"==typeof t.constructor&&("function"==typeof t.stream||"function"==typeof t.arrayBuffer)&&/^(Blob|File)$/.test(t[Symbol.toStringTag])}export class Sink{constructor(t,i){this._fileHandle=t,this._size=i,this._position=0}async abort(){await this._fileHandle.close()}async write(t){if("object"==typeof t)if("write"===t.type){if(Number.isInteger(t.position)&&t.position>=0&&(this._position=t.position),!("data"in t))throw await this._fileHandle.close(),new DOMException(...SYNTAX("write requires a data argument"));t=t.data}else{if("seek"===t.type){if(Number.isInteger(t.position)&&t.position>=0){if(this._size<t.position)throw new DOMException(...INVALID);return void(this._position=t.position)}throw await this._fileHandle.close(),new DOMException(...SYNTAX("seek requires a position argument"))}if("truncate"===t.type){if(Number.isInteger(t.size)&&t.size>=0)return await this._fileHandle.truncate(t.size),this._size=t.size,void(this._position>this._size&&(this._position=this._size));throw await this._fileHandle.close(),new DOMException(...SYNTAX("truncate requires a size argument"))}}if(t instanceof ArrayBuffer)t=new Uint8Array(t);else if("string"==typeof t)t=Buffer.from(t);else if(isBlob(t)){for await(const i of t.stream()){const t=await this._fileHandle.writev([i],this._position);this._position+=t.bytesWritten,this._size+=t.bytesWritten}return}const i=await this._fileHandle.writev([t],this._position);this._position+=i.bytesWritten,this._size+=i.bytesWritten}async close(){await this._fileHandle.close()}}export class FileHandle{constructor(t,i){this._path=t,this.name=i,this.kind="file"}async getFile(){await fs.stat(this._path).catch((t=>{if("ENOENT"===t.code)throw new DOMException(...GONE)}));const{fileFrom:t}=await import("fetch-blob/from.js");return t(this._path)}async isSameEntry(t){return this._path===this._getPath.apply(t)}_getPath(){return this._path}async createWritable(t){const i=await fs.open(this._path,t.keepExistingData?"r+":"w+").catch((t=>{if("ENOENT"===t.code)throw new DOMException(...GONE);throw t})),{size:e}=await i.stat();return new Sink(i,e)}}export class FolderHandle{_path="";constructor(t="",i=""){this.name=i,this.kind="directory",this._path=t}async isSameEntry(t){return this._path===t._path}async*entries(){const t=this._path,i=await fs.readdir(t).catch((t=>{if("ENOENT"===t.code)throw new DOMException(...GONE);throw t}));for(let e of i){const i=join(t,e),o=await fs.lstat(i);o.isFile()?yield[e,new FileHandle(i,e)]:o.isDirectory()&&(yield[e,new FolderHandle(i,e)])}}async getDirectoryHandle(t,i){const e=join(this._path,t),o=await fs.lstat(e).catch((t=>{if("ENOENT"!==t.code)throw t})),s=o?.isDirectory();if(o&&s)return new FolderHandle(e,t);if(o&&!s)throw new DOMException(...MISMATCH);if(!i.create)throw new DOMException(...GONE);return await fs.mkdir(e),new FolderHandle(e,t)}async getFileHandle(t,i){const e=join(this._path,t),o=await fs.lstat(e).catch((t=>{if("ENOENT"!==t.code)throw t})),s=o?.isFile();if(o&&s)return new FileHandle(e,t);if(o&&!s)throw new DOMException(...MISMATCH);if(!i.create)throw new DOMException(...GONE);return await(await fs.open(e,"w")).close(),new FileHandle(e,t)}async queryPermission(){return"granted"}async removeEntry(t,i){const e=join(this._path,t);(await fs.lstat(e).catch((t=>{if("ENOENT"===t.code)throw new DOMException(...GONE);throw t}))).isDirectory()?i.recursive?await fs.rm(e,{recursive:!0}).catch((t=>{if("ENOTEMPTY"===t.code)throw new DOMException(...MOD_ERR);throw t})):await fs.rmdir(e).catch((t=>{if("ENOTEMPTY"===t.code)throw new DOMException(...MOD_ERR);throw t})):await fs.unlink(e)}}export default t=>new FolderHandle(t);

View File

@ -0,0 +1 @@
import{errors}from"../util.js";const{DISALLOWED:DISALLOWED}=errors;class Sink{constructor(e,i){this.writer=e,this.fileEntry=i}async write(e){if("object"==typeof e)if("write"===e.type){if(Number.isInteger(e.position)&&e.position>=0&&(this.writer.seek(e.position),this.writer.position!==e.position&&(await new Promise(((i,t)=>{this.writer.onwriteend=i,this.writer.onerror=t,this.writer.truncate(e.position)})),this.writer.seek(e.position))),!("data"in e))throw new DOMException("Failed to execute 'write' on 'UnderlyingSinkBase': Invalid params passed. write requires a data argument","SyntaxError");e=e.data}else{if("seek"===e.type){if(Number.isInteger(e.position)&&e.position>=0){if(this.writer.seek(e.position),this.writer.position!==e.position)throw new DOMException("seeking position failed","InvalidStateError");return}throw new DOMException("Failed to execute 'write' on 'UnderlyingSinkBase': Invalid params passed. seek requires a position argument","SyntaxError")}if("truncate"===e.type)return new Promise((i=>{if(!(Number.isInteger(e.size)&&e.size>=0))throw new DOMException("Failed to execute 'write' on 'UnderlyingSinkBase': Invalid params passed. truncate requires a size argument","SyntaxError");this.writer.onwriteend=e=>i(),this.writer.truncate(e.size)}))}await new Promise(((i,t)=>{this.writer.onwriteend=i,this.writer.onerror=t,this.writer.write(new Blob([e]))}))}close(){return new Promise(this.fileEntry.file.bind(this.fileEntry))}}export class FileHandle{constructor(e,i=!0){this.file=e,this.kind="file",this.writable=i,this.readable=!0}get name(){return this.file.name}isSameEntry(e){return this.file.toURL()===e.file.toURL()}getFile(){return new Promise(this.file.file.bind(this.file))}createWritable(e){if(!this.writable)throw new DOMException(...DISALLOWED);return new Promise(((i,t)=>this.file.createWriter((t=>{!1===e.keepExistingData?(t.onwriteend=e=>i(new Sink(t,this.file)),t.truncate(0)):i(new Sink(t,this.file))}),t)))}}export class FolderHandle{constructor(e,i=!0){this.dir=e,this.writable=i,this.readable=!0,this.kind="directory",this.name=e.name}isSameEntry(e){return this.dir.fullPath===e.dir.fullPath}async*entries(){const e=this.dir.createReader(),i=await new Promise(e.readEntries.bind(e));for(const e of i)yield[e.name,e.isFile?new FileHandle(e,this.writable):new FolderHandle(e,this.writable)]}getDirectoryHandle(e,i){return new Promise(((t,r)=>{this.dir.getDirectory(e,i,(e=>{t(new FolderHandle(e))}),r)}))}getFileHandle(e,i){return new Promise(((t,r)=>this.dir.getFile(e,i,(e=>t(new FileHandle(e))),r)))}async removeEntry(e,i){const t=await this.getDirectoryHandle(e,{create:!1}).catch((i=>"TypeMismatchError"===i.name?this.getFileHandle(e,{create:!1}):i));if(t instanceof Error)throw t;return new Promise(((e,r)=>{t instanceof FolderHandle?i.recursive?t.dir.removeRecursively((()=>e()),r):t.dir.remove((()=>e()),r):t.file&&t.file.remove((()=>e()),r)}))}}export default(e={})=>new Promise(((i,t)=>window.webkitRequestFileSystem(e._persistent,0,(e=>i(new FolderHandle(e.root))),t)));

View File

@ -0,0 +1 @@
const config={ReadableStream:globalThis.ReadableStream,WritableStream:globalThis.WritableStream,TransformStream:globalThis.TransformStream,DOMException:globalThis.DOMException,Blob:globalThis.Blob,File:globalThis.File};export default config;

View File

@ -0,0 +1 @@
import showDirectoryPicker from"./showDirectoryPicker.js";import showOpenFilePicker from"./showOpenFilePicker.js";import showSaveFilePicker from"./showSaveFilePicker.js";import getOriginPrivateDirectory from"./getOriginPrivateDirectory.js";import FileSystemWritableFileStream from"./FileSystemWritableFileStream.js";import FileSystemDirectoryHandle from"./FileSystemDirectoryHandle.js";import FileSystemFileHandle from"./FileSystemFileHandle.js";import FileSystemHandle from"./FileSystemHandle.js";export{FileSystemDirectoryHandle,FileSystemFileHandle,FileSystemHandle,FileSystemWritableFileStream,getOriginPrivateDirectory,showDirectoryPicker,showOpenFilePicker,showSaveFilePicker};

View File

@ -0,0 +1 @@
async function getOriginPrivateDirectory(e,t={}){if(!e)return globalThis.navigator?.storage?.getDirectory()||globalThis.getOriginPrivateDirectory();const{FileSystemDirectoryHandle:i}=await import("./FileSystemDirectoryHandle.js"),r=await e;return new i(await(r.default?r.default(t):r(t)))}globalThis.DataTransferItem&&!DataTransferItem.prototype.getAsFileSystemHandle&&(DataTransferItem.prototype.getAsFileSystemHandle=async function(){const e=this.webkitGetAsEntry(),[{FileHandle:t,FolderHandle:i},{FileSystemDirectoryHandle:r},{FileSystemFileHandle:a}]=await Promise.all([import("./adapters/sandbox.js"),import("./FileSystemDirectoryHandle.js"),import("./FileSystemFileHandle.js")]);return e.isFile?new a(new t(e,!1)):new r(new i(e,!1))});export default getOriginPrivateDirectory;

View File

@ -0,0 +1 @@
const native=globalThis.showDirectoryPicker;async function showDirectoryPicker(e={}){if(native&&!e._preferPolyfill)return native(e);const t=document.createElement("input");t.type="file",t.webkitdirectory=!0,t.multiple=!0,t.style.position="fixed",t.style.top="-100000px",t.style.left="-100000px",document.body.appendChild(t);const i=import("./util.js");return await new Promise((e=>{t.addEventListener("change",e),t.click()})),i.then((e=>e.getDirHandlesFromInput(t)))}export default showDirectoryPicker;export{showDirectoryPicker};

View File

@ -0,0 +1 @@
const def={accepts:[]},native=globalThis.showOpenFilePicker;async function showOpenFilePicker(e={}){const t={...def,...e};if(native&&!e._preferPolyfill)return native(t);const i=document.createElement("input");i.type="file",i.multiple=t.multiple,i.accept=(t.accepts||[]).map((e=>[...(e.extensions||[]).map((e=>"."+e)),...e.mimeTypes||[]])).flat().join(","),Object.assign(i.style,{position:"fixed",top:"-100000px",left:"-100000px"}),document.body.appendChild(i);const n=import("./util.js");return await new Promise((e=>{i.addEventListener("change",e,{once:!0}),i.click()})),i.remove(),n.then((e=>e.getFileHandlesFromInput(i)))}export default showOpenFilePicker;export{showOpenFilePicker};

View File

@ -0,0 +1 @@
const native=globalThis.showSaveFilePicker;async function showSaveFilePicker(e={}){if(native&&!e._preferPolyfill)return native(e);e._name&&(console.warn("deprecated _name, spec now have `suggestedName`"),e.suggestedName=e._name);const{FileSystemFileHandle:a}=await import("./FileSystemFileHandle.js"),{FileHandle:i}=await import("./adapters/downloader.js");return new a(new i(e.suggestedName))}export default showSaveFilePicker;export{showSaveFilePicker};

View File

@ -0,0 +1 @@
export const errors={INVALID:["seeking position failed.","InvalidStateError"],GONE:["A requested file or directory could not be found at the time an operation was processed.","NotFoundError"],MISMATCH:["The path supplied exists, but was not an entry of requested type.","TypeMismatchError"],MOD_ERR:["The object can not be modified in this way.","InvalidModificationError"],SYNTAX:e=>[`Failed to execute 'write' on 'UnderlyingSinkBase': Invalid params passed. ${e}`,"SyntaxError"],SECURITY:["It was determined that certain files are unsafe for access within a Web application, or that too many calls are being made on file resources.","SecurityError"],DISALLOWED:["The request is not allowed by the user agent or the platform in the current context.","NotAllowedError"]};export const config={writable:globalThis.WritableStream};export async function fromDataTransfer(e){console.warn("deprecated fromDataTransfer - use `dt.items[0].getAsFileSystemHandle()` instead");const[t,r,a]=await Promise.all([import("./adapters/memory.js"),import("./adapters/sandbox.js"),import("./FileSystemDirectoryHandle.js")]),n=new t.FolderHandle("",!1);return n._entries=e.map((e=>e.isFile?new r.FileHandle(e,!1):new r.FolderHandle(e,!1))),new a.FileSystemDirectoryHandle(n)}export async function getDirHandlesFromInput(e){const{FolderHandle:t,FileHandle:r}=await import("./adapters/memory.js"),{FileSystemDirectoryHandle:a}=await import("./FileSystemDirectoryHandle.js"),n=Array.from(e.files),i=n[0].webkitRelativePath.split("/",1)[0],o=new t(i,!1);return n.forEach((e=>{const a=e.webkitRelativePath.split("/");a.shift();const n=a.pop();a.reduce(((e,r)=>(e._entries[r]||(e._entries[r]=new t(r,!1)),e._entries[r])),o)._entries[n]=new r(e.name,e,!1)})),new a(o)}export async function getFileHandlesFromInput(e){const{FileHandle:t}=await import("./adapters/memory.js"),{FileSystemFileHandle:r}=await import("./FileSystemFileHandle.js");return Array.from(e.files).map((e=>new r(new t(e.name,e,!1))))}

View File

@ -0,0 +1 @@
const WRITE=0,PULL=0,ERROR=1,ABORT=1,CLOSE=2,PING=3;class MessagePortSource{controller;constructor(e){this.port=e,this.port.onmessage=e=>this.onMessage(e.data)}start(e){this.controller=e}pull(){this.port.postMessage({type:0})}cancel(e){this.port.postMessage({type:1,reason:e.message}),this.port.close()}onMessage(e){0===e.type&&this.controller.enqueue(e.chunk),1===e.type&&(this.controller.error(e.reason),this.port.close()),2===e.type&&(this.controller.close(),this.port.close())}}self.addEventListener("install",(()=>{self.skipWaiting()})),self.addEventListener("activate",(e=>{e.waitUntil(self.clients.claim())}));const map=new Map;globalThis.addEventListener("message",(e=>{const t=e.data;t.url&&t.readablePort&&(t.rs=new ReadableStream(new MessagePortSource(e.data.readablePort),new CountQueuingStrategy({highWaterMark:4})),map.set(t.url,t))})),globalThis.addEventListener("fetch",(e=>{const t=e.request.url,s=map.get(t);if(!s)return null;map.delete(t),e.respondWith(new Response(s.rs,{headers:s.headers}))}));

View File

@ -21,9 +21,9 @@
overflow: auto;
max-width: 100%;
font-family: Consolas, "Andale Mono WT", "Andale Mono", "Lucida Console",
"Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono",
"Liberation Mono", "Nimbus Mono L", Monaco, "Courier New", Courier,
monospace;
"Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono",
"Liberation Mono", "Nimbus Mono L", Monaco, "Courier New", Courier,
monospace;
font-size: 14px;
tab-size: 4;
border-radius: 4px;

View File

@ -1,5 +1,7 @@
@import "colors";
nav.navbar {
background-color: hsl(203, 75%, 40%);
background-color: $primary-dark-color;
margin: 1em;
color: white;
border-radius: 0.6em;
@ -43,7 +45,7 @@ nav.navbar {
justify-content: center;
display: flex !important;
}
> .menu,
> .link {
box-sizing: border-box;
@ -146,5 +148,5 @@ nav.navbar {
}
}
}
}
}
}

View File

@ -0,0 +1,37 @@
@import "colors";
.pagination {
text-align: center;
gap: 10px;
button {
background-color: $secondary-neutral-light-color;
min-width: 37px;
&:hover {
background-color: darken($secondary-neutral-light-color, 10%);
}
&:disabled {
color: #fff;
background-color: darken($secondary-neutral-light-color, 5%);
}
&.active,
&.active:hover {
background-color: $primary-neutral-color;
color: white;
border-radius: 50%;
}
&:first-of-type,
&:last-of-type {
// previous and next buttons
&:disabled {
// Hide the arrows when they are disabled, without
// changing the layout of the navigation
opacity: 0;
}
}
}
}

View File

@ -1,39 +1,9 @@
$first-color: hsl(220, 100%, 50%);
$second-color: hsl(48, 100%, 67%);
$primary-color: hsl(219.9, 53.7%, 50%);
$secondary-color: hsl(204, 64%, 44%);
$primary-color-text: hsl(0, 0%, 100%);
$secondary-color-text: hsla(0, 0%, 0%, 0.87);
$primary-light-color: hsl(219.8, 46.4%, 64.9%);
$primary-dark-color: hsl(203, 75%, 40%);
$secondary-light-color: hsl(40, 68%, 65%);
$secondary-dark-color: hsl(40, 68%, 35%);
$primary-neutral-color: hsl(219.6, 20.8%, 50%);
$primary-neutral-light-color: hsl(0, 0%, 94%);
$primary-neutral-dark-color: hsl(210, 29%, 29%);
$secondary-neutral-color: hsl(204, 64%, 44%);
$secondary-neutral-light-color: hsl(0, 0%, 91%);
$secondary-neutral-dark-color: hsl(40, 57.6%, 17%);
$white-color: hsl(219.6, 20.8%, 98%);
$black-color: hsl(0, 0%, 17%);
$faceblue: hsl(221, 44%, 41%);
$twitblue: hsl(206, 82%, 63%);
$shadow-color: rgb(223, 223, 223);
$background-button-color: hsl(0, 0%, 95%);
@import "colors";
/*--------------------------MEDIA QUERY HELPERS------------------------*/
$small-devices: 576px;
$medium-devices: 768px;
$large-devices: 992px;
$extra-large-devices: 1200px;
/*--------------------------------GENERAL------------------------------*/
@ -43,17 +13,6 @@ body {
font-family: sans-serif;
}
button:disabled,
button:disabled:hover {
color: #fff;
background-color: #6c757d;
}
button.active,
button.active:hover {
color: #fff;
background-color: $secondary-color;
}
a.button,
button,
@ -170,7 +129,7 @@ a:not(.button) {
.shadow {
box-shadow: rgba(60, 64, 67, 0.3) 0 1px 3px 0,
rgba(60, 64, 67, 0.15) 0 4px 8px 3px;
rgba(60, 64, 67, 0.15) 0 4px 8px 3px;
}
.w_big {
@ -246,7 +205,7 @@ a:not(.button) {
#page {
width: 90%;
margin: 20px auto 0;
/*---------------------------------NAV---------------------------------*/
/*---------------------------------NAV---------------------------------*/
.btn {
font-size: 15px;
@ -278,7 +237,7 @@ a:not(.button) {
}
}
/*--------------------------------CONTENT------------------------------*/
/*--------------------------------CONTENT------------------------------*/
#quick_notif {
width: 100%;
margin: 0 auto;
@ -357,7 +316,7 @@ a:not(.button) {
}
}
/*---------------------------------NEWS--------------------------------*/
/*---------------------------------NEWS--------------------------------*/
#news {
display: flex;
@ -385,11 +344,11 @@ a:not(.button) {
background: $second-color;
box-shadow: $shadow-color 1px 1px 1px;
padding: 0.4em;
margin: 0em 0em 0.5em 0em;
margin: 0 0 0.5em 0;
text-transform: uppercase;
font-size: 1.1em;
&:not(:first-of-type) {
margin: 2em 0em 1em 0em;
margin: 2em 0 1em 0;
}
}
}
@ -400,7 +359,7 @@ a:not(.button) {
}
}
/* AGENDA/BIRTHDAYS */
/* AGENDA/BIRTHDAYS */
#agenda,
#birthdays {
display: block;
@ -410,7 +369,7 @@ a:not(.button) {
margin-bottom: 1em;
#agenda_title,
#birthdays_title {
margin: 0em;
margin: 0;
border-radius: 5px 5px 0 0;
box-shadow: $shadow-color 1px 1px 1px;
padding: 0.5em;
@ -444,7 +403,7 @@ a:not(.button) {
}
}
ul.birthdays_year {
margin: 0em;
margin: 0;
list-style-type: none;
font-weight: bold;
> li {
@ -454,7 +413,7 @@ a:not(.button) {
}
}
ul {
margin: 0em;
margin: 0;
margin-left: 1em;
list-style-type: square;
list-style-position: inside;
@ -463,9 +422,9 @@ a:not(.button) {
}
}
}
/* END AGENDA/BIRTHDAYS */
/* END AGENDA/BIRTHDAYS */
/* EVENTS TODAY AND NEXT FEW DAYS */
/* EVENTS TODAY AND NEXT FEW DAYS */
.news_events_group {
box-shadow: $shadow-color 1px 1px 1px;
margin-left: 1em;
@ -516,14 +475,14 @@ a:not(.button) {
float: left;
min-width: 7em;
max-width: 9em;
margin: 0em;
margin: 0;
margin-right: 1em;
margin-top: 0.8em;
img {
max-height: 6em;
max-width: 8em;
display: block;
margin: 0em auto;
margin: 0 auto;
}
}
.news_date {
@ -544,15 +503,15 @@ a:not(.button) {
}
}
}
/* END EVENTS TODAY AND NEXT FEW DAYS */
/* END EVENTS TODAY AND NEXT FEW DAYS */
/* COMING SOON */
/* COMING SOON */
.news_coming_soon {
display: list-item;
list-style-type: square;
list-style-position: inside;
margin-left: 1em;
padding-left: 0em;
padding-left: 0;
a {
font-weight: bold;
text-transform: uppercase;
@ -561,35 +520,35 @@ a:not(.button) {
font-size: 0.9em;
}
}
/* END COMING SOON */
/* END COMING SOON */
/* NOTICES */
/* NOTICES */
.news_notice {
margin: 0em 0em 1em 1em;
margin: 0 0 1em 1em;
padding: 0.4em;
padding-left: 1em;
background: $secondary-neutral-light-color;
box-shadow: $shadow-color 0 0 2px;
border-radius: 18px 5px 18px 5px;
h4 {
margin: 0em;
margin: 0;
}
.news_content {
margin-left: 1em;
}
}
/* END NOTICES */
/* END NOTICES */
/* CALLS */
/* CALLS */
.news_call {
margin: 0em 0em 1em 1em;
margin: 0 0 1em 1em;
padding: 0.4em;
padding-left: 1em;
background: $secondary-neutral-light-color;
border: 1px solid grey;
box-shadow: $shadow-color 1px 1px 1px;
h4 {
margin: 0em;
margin: 0;
}
.news_date {
font-size: 0.9em;
@ -598,7 +557,7 @@ a:not(.button) {
margin-left: 1em;
}
}
/* END CALLS */
/* END CALLS */
.news_empty {
margin-left: 1em;
@ -631,12 +590,12 @@ a:not(.button) {
width: 19%;
float: left;
min-width: 15em;
margin: 0em;
margin: 0;
img {
max-height: 15em;
max-width: 12em;
display: block;
margin: 0em auto;
margin: 0 auto;
margin-bottom: 10px;
}
}
@ -646,7 +605,6 @@ a:not(.button) {
padding: 0.5em 1em;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 1.2em;
border-radius: 2px;
float: right;
@ -978,7 +936,7 @@ table {
-moz-border-radius: 5px;
overflow: hidden;
box-shadow: rgba(60, 64, 67, 0.3) 0 1px 3px 0,
rgba(60, 64, 67, 0.15) 0 4px 8px 3px;
rgba(60, 64, 67, 0.15) 0 4px 8px 3px;
}
@media screen and (max-width: 500px) {
@ -1198,8 +1156,8 @@ u,
color: $primary-neutral-dark-color;
height: 100%;
width: 100%;
margin: 0em;
padding: 0em;
margin: 0;
padding: 0;
display: block;
}
}
@ -1393,7 +1351,7 @@ footer {
text-align: center;
vertical-align: middle;
div {
margin: 0.6em 0em;
margin: 0.6em 0;
color: $white-color;
border-radius: 5px;
display: flex;
@ -1507,514 +1465,3 @@ a.ui-button:active,
}
}
}
/* --------------------------------------pedagogy-----------------------------------*/
$pedagogy-blue: #1bb9ea;
$pedagogy-orange: #ea7900;
$pedagogy-hover-blue: #0e97ce;
$pedagogy-light-blue: #caf0ff;
$pedagogy-white-text: #f0f0f0;
.pedagogy {
#pagination {
text-align: center;
}
&.star-not-checked {
color: #f7f7f7;
margin-bottom: 0;
margin-top: 0;
}
&.star-checked {
color: $pedagogy-orange;
margin-bottom: 0;
margin-top: 0;
}
&.grade-without-star {
display: none;
}
@media screen and (max-width: $large-devices) {
&.star-not-checked {
margin-left: 5px;
margin-right: 5px;
}
&.star-checked {
margin-left: 5px;
margin-right: 5px;
}
}
@media screen and (max-width: $small-devices) {
&.grade-without-star {
display: block;
}
&.grade-with-star {
display: none;
}
}
#dynamic_view {
font-size: 1.1em;
overflow-wrap: break-word;
td {
text-align: center;
border: none;
}
}
#search_form {
.search-form-container {
display: grid;
grid-template-columns: auto auto;
grid-template-rows: auto auto auto;
grid-template-areas:
"action-bar action-bar"
"search-bar search-bar"
"radio-department radio-department"
"radio-credit-type radio-semester";
}
.action-bar {
grid-area: action-bar;
margin-bottom: 10px;
}
.search-bar {
grid-area: search-bar;
display: grid;
grid-template-columns: auto 200px;
grid-template-rows: auto;
grid-template-areas: "search-bar-input search-bar-button";
@media screen and (max-width: $medium-devices) {
grid-template-columns: auto auto;
grid-template-rows: auto;
grid-template-areas: "search-bar-input search-bar-button";
}
@media screen and (max-width: $small-devices) {
grid-template-columns: auto;
grid-template-rows: auto;
grid-template-areas: "search-bar-input";
.search-bar-button {
display: none;
}
}
.search-bar-input {
grid-area: search-bar-input;
background: $pedagogy-light-blue;
}
.search-bar-button {
grid-area: search-bar-button;
background: $pedagogy-orange;
color: white;
font-weight: bold;
margin-left: 20px;
}
}
.radio-department {
grid-area: radio-department;
}
.radio-credit-type {
grid-area: radio-credit-type;
}
.radio-semester {
grid-area: radio-semester;
}
.radio-guide input[type="radio"],
input[type="checkbox"] {
display: none;
}
.radio-guide {
margin-top: 10px;
color: white;
}
.radio-guide label {
display: inline-block;
background-color: $pedagogy-blue;
padding: 10px 20px;
font-family: Arial, sans-serif;
font-size: 16px;
border-radius: 4px;
}
.radio-guide input[type="radio"]:checked + label {
background-color: $pedagogy-orange;
}
.radio-guide input[type="checkbox"]:checked + label {
background-color: $pedagogy-orange;
}
.radio-guide label:hover {
background-color: $pedagogy-hover-blue;
}
}
#uv_detail {
color: #062f38;
.uv-quick-info-container {
display: grid;
grid-template-columns: 20% 20% 20% 20% auto;
grid-template-rows: auto auto;
grid-template-areas:
"hours-cm hours-td hours-tp hours-te hours-the"
"department credit-type semester . .";
}
.department {
grid-area: department;
}
.credit-type {
grid-area: credit-type;
}
.semester {
grid-area: semester;
}
.hours-cm {
grid-area: hours-cm;
}
.hours-td {
grid-area: hours-td;
}
.hours-tp {
grid-area: hours-tp;
}
.hours-te {
grid-area: hours-te;
}
.hours-the {
grid-area: hours-the;
}
#leave_comment_not_allowed {
p {
text-align: center;
color: red;
}
}
#leave_comment {
.leave-comment-grid-container {
display: grid;
grid-template-columns: 270px auto;
grid-template-rows: 100%;
grid-template-areas: "stars comment";
@media screen and (max-width: $large-devices) {
grid-template-columns: 100%;
grid-template-rows: auto auto;
grid-template-areas:
"stars"
"comment";
}
}
.ui-accordion-content {
background-color: $white-color;
border-color: $pedagogy-orange;
border-right: none;
}
.form-stars {
grid-area: stars;
}
.form-comment {
grid-area: comment;
}
.ui-accordion-header {
background-color: $pedagogy-orange;
color: $pedagogy-white-text;
clip-path: polygon(0 0%, 0 100%, 30% 100%, 33% 0);
@media screen and (max-width: $large-devices) {
clip-path: none;
}
}
.ui-accordion-header-icon {
color: $pedagogy-white-text;
margin-right: 10px;
}
.input-stars {
margin-top: 20px;
}
input[type="submit"] {
float: right;
}
}
.uv-details-container {
display: grid;
grid-template-columns: 150px 100px auto;
grid-template-rows: 156px 1fr;
grid-template-areas:
"grade grade-stars uv-infos"
". . uv-infos";
@media screen and (max-width: $large-devices) {
grid-template-columns: 50% 50%;
grid-template-rows: auto auto;
grid-template-areas:
"grade grade-stars"
"uv-infos uv-infos";
}
}
.grade {
grid-area: grade;
color: $pedagogy-white-text;
background-color: $pedagogy-blue;
padding-right: 10px;
> p {
text-align: right;
font-weight: bold;
}
}
.grade-stars {
grid-area: grade-stars;
color: $pedagogy-white-text;
background-color: $pedagogy-blue;
font-weight: bold;
}
.uv-infos {
grid-area: uv-infos;
padding-left: 10px;
}
.comment-container {
display: grid;
grid-template-columns: 300px auto;
grid-template-rows: auto auto auto;
grid-template-areas:
"grade-block comment"
"grade-block info"
"comment-end-bar comment-end-bar";
margin-bottom: 30px;
margin-top: 10px;
@media screen and (max-width: $large-devices) {
grid-template-columns: auto;
grid-template-rows: auto auto auto auto;
grid-template-areas:
"grade-block"
"comment"
"info"
"comment-end-bar";
}
.grade-block {
grid-area: grade-block;
width: 300px;
display: grid;
grid-template-columns: 150px 150px;
grid-template-rows: 156px auto;
grid-template-areas:
"grade-type grade-stars"
"grade-extension grade-extension";
grid-gap: 15px;
clip-path: polygon(0 0, 0 100%, 100% 100%, 100% 30px, 270px 0);
align-items: start;
background-color: $pedagogy-blue;
@media screen and (max-width: $large-devices) {
grid-template-columns: 50% auto;
grid-template-rows: auto;
grid-template-areas: "grade-type grade-stars";
width: auto;
clip-path: none;
align-content: space-evenly;
align-items: end;
}
.grade-extension {
grid-area: grade-extension;
background-color: $pedagogy-blue;
}
.grade-type {
grid-area: grade-type;
> p {
color: $pedagogy-white-text;
font-weight: bold;
text-align: right;
}
}
.grade-stars {
grid-area: grade-stars;
}
}
.comment {
grid-area: comment;
display: grid;
grid-template-columns: auto;
grid-template-rows: auto auto;
grid-template-areas:
"anchor"
"markdown";
@media screen and (max-width: $large-devices) {
border-left: solid;
border-right: solid;
border-color: $pedagogy-blue;
}
.anchor {
grid-area: anchor;
text-align: right;
margin-right: 15px;
}
.markdown {
grid-area: markdown;
min-height: 139px;
margin-top: 0;
margin-right: 0;
padding: 10px;
text-align: justify;
overflow: auto;
}
}
.info {
grid-area: info;
padding-bottom: 10px;
@media screen and (max-width: $large-devices) {
border-left: solid;
border-right: solid;
border-color: $pedagogy-blue;
}
.status-reported {
color: red;
float: left;
padding-left: 10px;
}
.actions {
float: right;
}
}
.comment-end-bar {
grid-area: comment-end-bar;
display: grid;
grid-template-columns: 33% auto auto;
grid-template-rows: 2.5em;
grid-template-areas: "author date report";
background-color: $pedagogy-blue;
margin-top: -1px;
@media screen and (max-width: $large-devices) {
grid-template-columns: auto;
grid-template-rows: auto auto auto;
grid-template-areas:
"report"
"date"
"author";
margin-top: 0;
text-align: center;
}
.author {
grid-area: author;
padding-top: 6px;
padding-left: 20px;
background-color: $pedagogy-orange;
clip-path: polygon(0 10px, 0 100%, 350px 200%, 300px 10px);
@media screen and (max-width: $large-devices) {
clip-path: none;
padding: 0;
padding-bottom: 7px;
}
a {
color: $pedagogy-white-text;
font-weight: bold;
}
a:hover {
color: $pedagogy-hover-blue;
}
}
.date {
grid-area: date;
color: $pedagogy-white-text;
@media screen and (max-width: $large-devices) {
padding-bottom: 7px;
}
}
.report {
grid-area: report;
justify-self: right;
padding-right: 30px;
padding-left: 30px;
a {
color: $pedagogy-white-text;
}
a:hover {
color: $pedagogy-hover-blue;
}
@media screen and (max-width: $large-devices) {
text-align: center;
justify-self: inherit;
padding-bottom: 7px;
background-color: $white-color;
border-left: solid;
border-right: solid;
border-color: $pedagogy-blue;
a {
color: $black-color;
}
}
}
}
}
}
}

View File

@ -5,287 +5,287 @@ $border: .01rem solid black;
$min_col_width: 100px;
.error {
color: red !important;
color: red !important;
}
.radio-btn {
display: flex;
flex-direction: row;
gap: $gap;
display: flex;
flex-direction: row;
gap: $gap;
> input,
> label {
margin: 0;
}
> input,
> label {
margin: 0;
}
&:hover {
cursor: pointer;
}
&:hover {
cursor: pointer;
}
}
.election_vote {
overflow-x: scroll !important;
overflow-x: scroll !important;
}
.election_table {
width: 100%;
width: 100%;
>.lists {
>.lists {
display: flex;
flex-direction: row;
>tr {
display: flex;
flex-direction: row;
width: 100%;
>.column {
display: flex;
flex-direction: column-reverse;
align-items: center;
justify-content: center;
padding: $padding;
border: $border;
border-collapse: collapse;
position: relative;
min-width: $min_col_width;
>a{
margin-left: $padding;
width: 20px;
height: 20px;
text-align: center;
padding: 5px;
border-radius: 25%;
margin: 0;
display: flex;
align-items: center;
justify-content: center;
position: absolute;
right: $gap;
top: $gap;
&:hover {
background-color: #ddd;
}
}
}
}
}
>.role {
display: flex;
flex-direction: column;
>tr {
display: flex;
flex-direction: row;
background-color: lightgrey;
&:hover {
background-color: lightgrey;
}
>.role_title {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
margin: 0;
padding: $padding;
width: 100%;
>tr {
display: flex;
flex-direction: row;
width: 100%;
>.column {
display: flex;
flex-direction: column-reverse;
align-items: center;
justify-content: center;
padding: $padding;
border: $border;
border-collapse: collapse;
position: relative;
min-width: $min_col_width;
>a{
margin-left: $padding;
width: 20px;
height: 20px;
text-align: center;
padding: 5px;
border-radius: 25%;
margin: 0;
display: flex;
align-items: center;
justify-content: center;
position: absolute;
right: $gap;
top: $gap;
&:hover {
background-color: #ddd;
}
}
}
>.role_text {
>h4 {
margin: 0;
}
>p {
margin-top: .5em;
}
}
}
>.role {
>.role_buttons {
display: flex;
flex-direction: row;
align-items: center;
gap: $gap;
> button,
> button > i,
> a {
width: 20px;
height: 20px;
background-color: #e9e9e9;
text-align: center;
padding: 5px;
border-radius: 25%;
margin: 0;
display: flex;
align-items: center;
justify-content: center;
&:hover,
&:hover > i {
background-color: #fff;
}
}
> button {
width: 30px;
height: 30px;
}
> button[disabled] {
background-color: #eee;
cursor: not-allowed;
>i,
&:hover,
&:hover > i {
background-color: #eee;
}
}
}
}
>.list_per_role {
display: flex;
flex-direction: column;
flex-direction: row;
justify-content: center;
border: $border;
border-collapse: collapse;
background-color: #fff;
padding: $padding_smaller;
margin: 0;
min-width: $min_col_width;
>tr {
>.candidates {
margin: 0;
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
width: 100%;
gap: $gap;
>.candidate {
display: flex;
flex-direction: row;
background-color: lightgrey;
flex-direction: column;
align-items: center;
list-style-type: none;
width: 100%;
gap: $gap;
&:hover {
background-color: lightgrey;
}
>input[type="radio"]:checked + label,
>input[type="checkbox"]:checked + label {
background-color: lightgray;
border-radius: 10px;
>.role_title {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
margin: 0;
padding: $padding;
width: 100%;
>.role_text {
>h4 {
margin: 0;
}
>p {
margin-top: .5em;
}
}
>.role_buttons {
display: flex;
flex-direction: row;
align-items: center;
gap: $gap;
> button,
> button > i,
> a {
width: 20px;
height: 20px;
background-color: #e9e9e9;
text-align: center;
padding: 5px;
border-radius: 25%;
margin: 0;
display: flex;
align-items: center;
justify-content: center;
&:hover,
&:hover > i {
background-color: #fff;
}
}
> button {
width: 30px;
height: 30px;
}
> button[disabled] {
background-color: #eee;
cursor: not-allowed;
>i,
&:hover,
&:hover > i {
background-color: #eee;
}
}
}
}
>.list_per_role {
display: flex;
flex-direction: row;
justify-content: center;
border: $border;
border-collapse: collapse;
>figure>.edit_btns>a:hover{
background-color: #fff;
padding: $padding_smaller;
margin: 0;
min-width: $min_col_width;
>.candidates {
margin: 0;
display: flex;
flex-direction: row;
flex-wrap: wrap;
justify-content: center;
width: 100%;
gap: $gap;
>.candidate {
display: flex;
flex-direction: column;
align-items: center;
list-style-type: none;
width: 100%;
gap: $gap;
>input[type="radio"]:checked + label,
>input[type="checkbox"]:checked + label {
background-color: lightgray;
border-radius: 10px;
>figure>.edit_btns>a:hover{
background-color: #fff;
}
}
>label {
width: 100%;
}
>label>figure,
>figure {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
gap: $gap;
padding: 10px;
max-width: 100%;
>img {
max-width: 100% !important;
}
>figcaption {
width: 100%;
max-width: inherit !important;
overflow: hidden;
h5 {
margin: 0;
text-align: center;
}
.candidate_program {
margin: 5px 0;
}
}
>.edit_btns {
position: absolute;
display: flex;
flex-direction: column;
top: $gap;
right: $gap;
gap: $gap;
> a {
width: 20px;
height: 20px;
background-color: #e9e9e9;
text-align: center;
padding: 5px;
border-radius: 25%;
margin: 0;
display: flex;
align-items: center;
justify-content: center;
&:hover {
background-color: #d8d8d8;
}
}
}
}
}
}
}
}
>label {
width: 100%;
}
>label>figure,
>figure {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
gap: $gap;
padding: 10px;
max-width: 100%;
>img {
max-width: 100% !important;
}
>figcaption {
width: 100%;
max-width: inherit !important;
overflow: hidden;
h5 {
margin: 0;
text-align: center;
}
.candidate_program {
margin: 5px 0;
}
}
>.edit_btns {
position: absolute;
display: flex;
flex-direction: column;
top: $gap;
right: $gap;
gap: $gap;
> a {
width: 20px;
height: 20px;
background-color: #e9e9e9;
text-align: center;
padding: 5px;
border-radius: 25%;
margin: 0;
display: flex;
align-items: center;
justify-content: center;
&:hover {
background-color: #d8d8d8;
}
}
}
}
}
}
}
}
}
}
.election_details {
margin: .5em 0;
margin: .5em 0;
}
.buttons {
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
justify-content: center;
gap: $gap;
display: flex;
flex-direction: row;
flex-wrap: wrap;
align-items: center;
justify-content: center;
gap: $gap;
}
.button {
border: none;
border: none;
color: black;
text-decoration: none;
background-color: #f2f2f2;
padding: 0.4em;
margin: 0.1em;
font-size: 1.18em;
border-radius: 5px;
box-shadow: #dfdfdf 0px 0px 1px;
cursor: pointer;
&:hover {
color: black;
text-decoration: none;
background-color: #f2f2f2;
padding: 0.4em;
margin: 0.1em;
font-size: 1.18em;
border-radius: 5px;
box-shadow: #dfdfdf 0px 0px 1px;
cursor: pointer;
background: #d4d4d4;
}
&_send {
background-color: #59aee2;
&:hover {
color: black;
background: #d4d4d4;
}
&_send {
background-color: #59aee2;
&:hover {
background-color: rgb(130, 186, 235);
}
background-color: rgb(130, 186, 235);
}
}
}

View File

@ -199,12 +199,6 @@
> form {
> p {
box-sizing: border-box;
> input {
width: 100%;
max-width: 100%;
box-sizing: border-box;
}
}
> .results_on_deck > div {
@ -219,12 +213,15 @@
right: 0;
}
}
> input {
width: 100%;
input {
min-width: 100%;
max-width: 100%;
box-sizing: border-box;
}
button {
font-weight: bold;
}
}
}
}

View File

@ -32,7 +32,6 @@
width: 100%;
}
// Django moment
> div.mini_profile_link {
position: relative;
@ -50,7 +49,7 @@
align-items: flex-start;
max-height: 65px;
}
> span {
height: 150px;
width: 100%;
@ -66,14 +65,14 @@
max-height: 100%;
height: auto;
object-fit: contain;
@media (max-width: 375px) {
max-width: 100%;
max-height: 65px;
}
}
}
> em {
box-sizing: border-box;
padding: 0 5px;
@ -106,7 +105,6 @@
}
}
// Django moment
> a.mini_profile_link {
display: none;
}

View File

@ -35,7 +35,7 @@ main {
> span {
margin-top: 5px;
}
> span > span,
> span {
display: flex;
@ -43,7 +43,7 @@ main {
flex-wrap: wrap;
gap: 5px;
width: 100%;
>.button {
font-size: smaller;
width: 100%;

View File

@ -2,9 +2,9 @@
{% block content %}
<h3>{% trans %}403, Forbidden{% endtrans %}</h3>
<h3>{% trans %}403, Forbidden{% endtrans %}</h3>
{{ super() }}
{{ super() }}
{% endblock %}

View File

@ -2,9 +2,9 @@
{% block content %}
<div id="page">
<h3>{% trans %}404, Not Found{% endtrans %}</h3>
</div>
<div id="page">
<h3>{% trans %}404, Not Found{% endtrans %}</h3>
</div>
{% endblock %}

View File

@ -1,27 +1,27 @@
{% extends "core/base.jinja" %}
{% block head %}
{{ super() }}
<script
src="https://browser.sentry-cdn.com/7.11.1/bundle.min.js"
integrity="sha384-qcYSo5+/E8hEkPmHFa79GRDsGT84SRhBJHRw3+dbQyh0UwueiFP1jCsRBClEREcs"
crossorigin="anonymous"
></script>
{{ super() }}
<script
src="https://browser.sentry-cdn.com/7.11.1/bundle.min.js"
integrity="sha384-qcYSo5+/E8hEkPmHFa79GRDsGT84SRhBJHRw3+dbQyh0UwueiFP1jCsRBClEREcs"
crossorigin="anonymous"
></script>
{% endblock head %}
{% block content %}
<h3>{% trans %}500, Server Error{% endtrans %}</h3>
{% if settings.SENTRY_DSN %}
<script>
Sentry.init({ dsn: '{{ settings.SENTRY_DSN }}' });
Sentry.showReportDialog({
eventId: '{{ request.sentry_last_event_id() }}',
{% if user.is_authenticated %}
user: {
'name': '{{user.first_name}} {{user.last_name}}',
'email': '{{user.email}}'
}
{% endif %}
})
</script>
{% endif %}
<h3>{% trans %}500, Server Error{% endtrans %}</h3>
{% if settings.SENTRY_DSN %}
<script>
Sentry.init({ dsn: '{{ settings.SENTRY_DSN }}' });
Sentry.showReportDialog({
eventId: '{{ request.sentry_last_event_id() }}',
{% if user.is_authenticated %}
user: {
'name': '{{user.first_name}} {{user.last_name}}',
'email': '{{user.email}}'
}
{% endif %}
})
</script>
{% endif %}
{% endblock content %}

View File

@ -1,342 +1,321 @@
<!DOCTYPE html>
<html lang="fr">
<head>
{% block head %}
<title>{% block title %}{% trans %}Welcome!{% endtrans %}{% endblock %} - Association des Étudiants UTBM</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" href="{{ static('core/img/favicon.ico') }}">
<link rel="stylesheet" href="{{ static('core/base.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="{{ scss('core/style.scss') }}">
<link rel="stylesheet" href="{{ scss('core/markdown.scss') }}">
<link rel="stylesheet" href="{{ scss('core/header.scss') }}">
<link rel="stylesheet" href="{{ scss('core/navbar.scss') }}">
<head>
{% block head %}
<title>{% block title %}{% trans %}Welcome!{% endtrans %}{% endblock %} - Association des Étudiants UTBM</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" href="{{ static('core/img/favicon.ico') }}">
<link rel="stylesheet" href="{{ static('core/base.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/markdown.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 #}
<link rel="stylesheet" href="{{ static('core/js/ui/jquery-ui.min.css') }}">
{% endblock %}
<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>
<script defer href="{{ static('core/font-awesome/js/fontawesone.min.js') }}"></script>
<link rel="stylesheet" href="{{ static('core/js/ui/jquery-ui.min.css') }}">
{% endblock %}
<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>
<script defer href="{{ static('core/font-awesome/js/fontawesone.min.js') }}"></script>
<!-- Jquery declared here to be accessible in every django widgets -->
<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 -->
<script src="{{ static('core/js/script.js') }}"></script>
<script src="{{ static('core/js/script.js') }}"></script>
{% block additional_css %}{% endblock %}
{% block additional_js %}{% endblock %}
{% endblock %}
</head>
{% block additional_css %}{% endblock %}
{% block additional_js %}{% endblock %}
{% endblock %}
</head>
<body>
<body>
<!-- The token is always passed here to be accessible from the dom -->
<!-- See this workaround https://docs.djangoproject.com/en/2.0/ref/csrf/#acquiring-the-token-if-csrf-use-sessions-is-true -->
{% csrf_token %}
{% csrf_token %}
<!-- BEGIN HEADER -->
{% block header %}
{% if not popup %}
<header class="header">
<div class="header-logo">
<a class="header-logo-picture" href="{{ url('core:index') }}" style="background-image: url('{{ static('core/img/logo_no_text.png') }}')">
&nbsp;
</a>
<a class="header-logo-text" href="{{ url('core:index') }}">
<span>Association des Étudiants</span>
<span>de l'Université de Technologie de Belfort-Montbéliard</span>
</a>
</div>
{% if not user.is_authenticated %}
<div class="header-disconnected">
<a class="button" href="{{ url('core:login') }}">{% trans %}Login{% endtrans %}</a>
<a class="button" href="{{ url('core:register') }}">{% trans %}Register{% endtrans %}</a>
</div>
{% else %}
<div class="header-connected">
<div class="left">
<form class="search" action="{{ url('core:search') }}" method="GET" id="header_search">
<input class="header-input" type="text" placeholder="{% trans %}Search{% endtrans %}" name="query" id="search" />
<input type="submit" value="{% trans %}Search{% endtrans %}" style="display: none;" />
</form>
<ul class="bars">
{% cache 100 "counters_activity" %}
{% for bar in Counter.objects.annotate_has_barman(user).filter(type="BAR") %}
<li>
{% block header %}
{% if not popup %}
<header class="header">
<div class="header-logo">
<a class="header-logo-picture" href="{{ url('core:index') }}" style="background-image: url('{{ static('core/img/logo_no_text.png') }}')">
&nbsp;
</a>
<a class="header-logo-text" href="{{ url('core:index') }}">
<span>Association des Étudiants</span>
<span>de l'Université de Technologie de Belfort-Montbéliard</span>
</a>
</div>
{% if not user.is_authenticated %}
<div class="header-disconnected">
<a class="button" href="{{ url('core:login') }}">{% trans %}Login{% endtrans %}</a>
<a class="button" href="{{ url('core:register') }}">{% trans %}Register{% endtrans %}</a>
</div>
{% else %}
<div class="header-connected">
<div class="left">
<form class="search" action="{{ url('core:search') }}" method="GET" id="header_search">
<input class="header-input" type="text" placeholder="{% trans %}Search{% endtrans %}" name="query" id="search" />
<input type="submit" value="{% trans %}Search{% endtrans %}" style="display: none;" />
</form>
<ul class="bars">
{% cache 100 "counters_activity" %}
{% for bar in Counter.objects.annotate_has_barman(user).filter(type="BAR") %}
<li>
{# If the user is a barman, we redirect him directly to the barman page
else we redirect him to the activity page #}
{% if bar.has_annotated_barman %}
<a href="{{ url('counter:details', counter_id=bar.id) }}">
{% else %}
<a href="{{ url('counter:activity', counter_id=bar.id) }}">
{% endif %}
{% if bar.is_inactive() %}
<i class="fa fa-question" style="color: #f39c12"></i>
{% elif bar.is_open %}
<i class="fa fa-check" style="color: #2ecc71"></i>
{% else %}
<i class="fa fa-times" style="color: #eb2f06"></i>
{% endif %}
<span>{{ bar }}</span>
</a>
</li>
{% endfor %}
{% endcache %}
</ul>
</div>
<div class="right">
<div class="user">
<div class="options">
<div class="username">
<a href="{{ url('core:user_profile', user_id=user.id) }}">{{ user.get_display_name() }}</a>
</div>
<div class="links">
<a href="{{ url('core:user_tools') }}">{% trans %}Tools{% endtrans %}</a>
<a href="{{ url('core:logout') }}">{% trans %}Logout{% endtrans %}</a>
</div>
</div>
<a
href="{{ url('core:user_profile', user_id=user.id) }}"
{% if user.profile_pict %}
style="background-image: url('{{ user.profile_pict.get_download_url() }}')"
{% else %}
style="background-image: url('{{ static('core/img/unknown.jpg') }}')"
{% endif %}
></a>
</div>
<div class="notification">
<a href="#" onclick="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') %}
<li>
<a href="{{ url("core:notification", notif_id=n.id) }}">
<div class="datetime">
<span class="header_notif_date">
{{ n.date|localtime|date(DATE_FORMAT) }}
</span>
<span class="header_notif_time">
{{ n.date|localtime|time(DATETIME_FORMAT) }}
</span>
</div>
<div class="reason">
{{ n }}
</div>
</a>
</li>
{% endfor %}
{% else %}
<li class="empty-notification">{% trans %}You do not have any unread notification{% endtrans %}</li>
{% endif %}
</ul>
<div class="options">
<a href="{{ url('core:notification_list') }}">
{% trans %}View more{% endtrans %}
</a>
<a href="{{ url('core:notification_list') }}?see_all">
{% trans %}Mark all as read{% endtrans %}
</a>
</div>
</div>
</div>
</div>
</div>
{% if bar.has_annotated_barman %}
<a href="{{ url('counter:details', counter_id=bar.id) }}">
{% else %}
<a href="{{ url('counter:activity', counter_id=bar.id) }}">
{% endif %}
{% if bar.is_inactive() %}
<i class="fa fa-question" style="color: #f39c12"></i>
{% elif bar.is_open %}
<i class="fa fa-check" style="color: #2ecc71"></i>
{% else %}
<i class="fa fa-times" style="color: #eb2f06"></i>
{% endif %}
<span>{{ bar }}</span>
</a>
</li>
{% endfor %}
{% endcache %}
</ul>
</div>
<div class="right">
<div class="user">
<div class="options">
<div class="username">
<a href="{{ url('core:user_profile', user_id=user.id) }}">{{ user.get_display_name() }}</a>
</div>
<div class="links">
<a href="{{ url('core:user_tools') }}">{% trans %}Tools{% endtrans %}</a>
<a href="{{ url('core:logout') }}">{% trans %}Logout{% endtrans %}</a>
</div>
</div>
<a
href="{{ url('core:user_profile', user_id=user.id) }}"
{% if user.profile_pict %}
style="background-image: url('{{ user.profile_pict.get_download_url() }}')"
{% else %}
style="background-image: url('{{ static('core/img/unknown.jpg') }}')"
{% endif %}
<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>
></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') %}
<li>
<a href="{{ url("core:notification", notif_id=n.id) }}">
<div class="datetime">
<span class="header_notif_date">
{{ n.date|localtime|date(DATE_FORMAT) }}
</span>
<span class="header_notif_time">
{{ n.date|localtime|time(DATETIME_FORMAT) }}
</span>
</div>
<div class="reason">
{{ n }}
</div>
</a>
</li>
{% endfor %}
{% else %}
<li class="empty-notification">{% trans %}You do not have any unread notification{% endtrans %}</li>
{% endif %}
</ul>
<div class="options">
<a href="{{ url('core:notification_list') }}">
{% trans %}View more{% endtrans %}
</a>
<a href="{{ url('core:notification_list') }}?see_all">
{% trans %}Mark all as read{% endtrans %}
</a>
</div>
</header>
</div>
</div>
</div>
</div>
{% endif %}
<div class="header-lang">
{% for language in LANGUAGES %}
<form action="{{ url('set_language') }}" method="post">
{% csrf_token %}
<input name="next" value="{{ request.path }}" type="hidden" />
<input name="language" value="{{ language[0] }}" type="hidden" />
<input type="submit" value="{% if language[0] == 'en' %}🇬🇧{% else %}🇫🇷{% endif %}" />
</form>
{% endfor %}
</div>
</header>
{% block info_boxes %}
<div id="info_boxes">
{% set sith = get_sith() %}
{% if sith.alert_msg %}
<div id="alert_box">
{{ sith.alert_msg|markdown }}
</div>
{% endif %}
{% if sith.info_msg %}
<div id="info_box">
{{ sith.info_msg|markdown }}
</div>
{% endif %}
</div>
{% endblock %}
{% else %}
<div id="popupheader">{{ user.get_display_name() }}</div>
{% block info_boxes %}
<div id="info_boxes">
{% set sith = get_sith() %}
{% if sith.alert_msg %}
<div id="alert_box">
{{ sith.alert_msg|markdown }}
</div>
{% endif %}
{% if sith.info_msg %}
<div id="info_box">
{{ sith.info_msg|markdown }}
</div>
{% endif %}
</div>
{% endblock %}
{% else %}
<div id="popupheader">{{ user.get_display_name() }}</div>
{% endif %}
{% endblock %}
<!-- END HEADER -->
{% block nav %}
{% if not popup %}
<nav class="navbar">
<button class="expand-button" onclick="showMenu()"><i class="fa fa-bars"></i></button>
<div id="navbar-content" class="content" style="display: none;">
<a class="link" href="{{ url('core:index') }}">{% trans %}Main{% endtrans %}</a>
<div class="menu">
<span class="head">{% trans %}Associations & Clubs{% endtrans %}</span>
<ul class="content">
<li><a href="{{ url('core:page', page_name='ae') }}">{% trans %}AE{% endtrans %}</a></li>
<li><a href="{{ url('core:page', page_name='clubs') }}">{% trans %}AE's clubs{% endtrans %}</a></li>
<li><a href="{{ url('core:page', page_name='utbm-associations') }}">{% trans %}Others UTBM's Associations{% endtrans %}</a></li>
</ul>
</div>
<div class="menu">
<span class="head">{% trans %}Events{% endtrans %}</span>
<ul class="content">
<li><a href="{{ url('election:list') }}">{% trans %}Elections{% endtrans %}</a></li>
<li><a href="{{ url('core:page', page_name='ga') }}">{% trans %}Big event{% endtrans %}</a></li>
</ul>
</div>
<a class="link" href="{{ url('forum:main') }}">{% trans %}Forum{% endtrans %}</a>
<a class="link" href="{{ url('sas:main') }}">{% trans %}Gallery{% endtrans %}</a>
<a class="link" href="{{ url('eboutic:main') }}">{% trans %}Eboutic{% endtrans %}</a>
<div class="menu">
<span class="head">{% trans %}Services{% endtrans %}</span>
<ul class="content">
<li><a href="{{ url('matmat:search_clear') }}">{% trans %}Matmatronch{% endtrans %}</a></li>
<li><a href="/launderette">{% trans %}Launderette{% endtrans %}</a></li>
<li><a href="{{ url('core:file_list') }}">{% trans %}Files{% endtrans %}</a></li>
<li><a href="{{ url('pedagogy:guide') }}">{% trans %}Pedagogy{% endtrans %}</a></li>
</ul>
</div>
<div class="menu">
<span class="head">{% trans %}My Benefits{% endtrans %}</span>
<ul class="content">
<li><a href="{{ url('core:page', page_name='partenaires')}}">{% trans %}Sponsors{% endtrans %}</a></li>
<li><a href="{{ url('core:page', page_name='avantages') }}">{% trans %}Subscriber benefits{% endtrans %}</a></li>
</ul>
</div>
<div class="menu">
<span class="head">{% trans %}Help{% endtrans %}</span>
<ul class="content">
<li><a href="{{ url('core:page', page_name='FAQ') }}">{% trans %}FAQ{% endtrans %}</a></li>
<li><a href="{{ url('core:page', 'contacts') }}">{% trans %}Contacts{% endtrans %}</a></li>
<li><a href="{{ url('core:page', page_name='Index') }}">{% trans %}Wiki{% endtrans %}</a></li>
</ul>
</div>
</div>
</nav>
{% endif %}
{% endblock %}
<div id="page">
<ul id="quick_notif">
{% for n in quick_notifs %}
<li>{{ n }}</li>
{% endfor %}
</ul>
<div id="content">
{% if list_of_tabs %}
<div class="tool_bar">
<div class="tools">
{% for t in list_of_tabs -%}
<a href="{{ t.url }}" {%- if current_tab==t.slug %} class="selected_tab" {%- endif -%}>{{ t.name }}</a>
{%- endfor %}
</div>
{% block nav %}
{% if not popup %}
<nav class="navbar">
<button class="expand-button" onclick="showMenu()"><i class="fa fa-bars"></i></button>
<div id="navbar-content" class="content" style="display: none;">
<a class="link" href="{{ url('core:index') }}">{% trans %}Main{% endtrans %}</a>
<div class="menu">
<span class="head">{% trans %}Associations & Clubs{% endtrans %}</span>
<ul class="content">
<li><a href="{{ url('core:page', page_name='ae') }}">{% trans %}AE{% endtrans %}</a></li>
<li><a href="{{ url('core:page', page_name='clubs') }}">{% trans %}AE's clubs{% endtrans %}</a></li>
<li><a href="{{ url('core:page', page_name='utbm-associations') }}">{% trans %}Others UTBM's Associations{% endtrans %}</a></li>
</ul>
</div>
<div class="menu">
<span class="head">{% trans %}Events{% endtrans %}</span>
<ul class="content">
<li><a href="{{ url('election:list') }}">{% trans %}Elections{% endtrans %}</a></li>
<li><a href="{{ url('core:page', page_name='ga') }}">{% trans %}Big event{% endtrans %}</a></li>
</ul>
</div>
<a class="link" href="{{ url('forum:main') }}">{% trans %}Forum{% endtrans %}</a>
<a class="link" href="{{ url('sas:main') }}">{% trans %}Gallery{% endtrans %}</a>
<a class="link" href="{{ url('eboutic:main') }}">{% trans %}Eboutic{% endtrans %}</a>
<div class="menu">
<span class="head">{% trans %}Services{% endtrans %}</span>
<ul class="content">
<li><a href="{{ url('matmat:search_clear') }}">{% trans %}Matmatronch{% endtrans %}</a></li>
<li><a href="/launderette">{% trans %}Launderette{% endtrans %}</a></li>
<li><a href="{{ url('core:file_list') }}">{% trans %}Files{% endtrans %}</a></li>
<li><a href="{{ url('pedagogy:guide') }}">{% trans %}Pedagogy{% endtrans %}</a></li>
</ul>
</div>
<div class="menu">
<span class="head">{% trans %}My Benefits{% endtrans %}</span>
<ul class="content">
<li><a href="{{ url('core:page', page_name='partenaires')}}">{% trans %}Sponsors{% endtrans %}</a></li>
<li><a href="{{ url('core:page', page_name='avantages') }}">{% trans %}Subscriber benefits{% endtrans %}</a></li>
</ul>
</div>
<div class="menu">
<span class="head">{% trans %}Help{% endtrans %}</span>
<ul class="content">
<li><a href="{{ url('core:page', page_name='FAQ') }}">{% trans %}FAQ{% endtrans %}</a></li>
<li><a href="{{ url('core:page', 'contacts') }}">{% trans %}Contacts{% endtrans %}</a></li>
<li><a href="{{ url('core:page', page_name='Index') }}">{% trans %}Wiki{% endtrans %}</a></li>
</ul>
</div>
{% endif %}
{% if error %}
{{ error }}
{% endif %}
{% block content %}
{% endblock %}
</div>
</div>
</nav>
{% endif %}
{% endblock %}
{% if not popup %}
<footer>
{% block footer %}
<div>
<a href="{{ url('core:page', 'contacts') }}">{% trans %}Contacts{% endtrans %}</a>
<a href="{{ url('core:page', 'legals') }}">{% trans %}Legal notices{% endtrans %}</a>
<a href="{{ url('core:page', 'copyright_agent') }}">{% trans %}Intellectual property{% endtrans %}</a>
<a href="{{ url('core:page', 'docs') }}">{% trans %}Help & Documentation{% endtrans %}</a>
<a href="{{ url('core:page', 'rd') }}">{% trans %}R&D{% endtrans %}</a>
</div>
<a href="https://discord.gg/XK9WfPsUFm" target="_link">
{% trans %}Site created by the IT Department of the AE{% endtrans %}
</a>
{% endblock %}
<br>
<code class="version">
{% cache 1000 "sith_version" %}
{% trans %}Sith version:{% endtrans %}&nbsp;{{ get_sith().version }}
{% endcache %}
</code>
</footer>
<div id="page">
<ul id="quick_notif">
{% for n in quick_notifs %}
<li>{{ n }}</li>
{% endfor %}
</ul>
<div id="content">
{% if list_of_tabs %}
<div class="tool_bar">
<div class="tools">
{% for t in list_of_tabs -%}
<a href="{{ t.url }}" {%- if current_tab==t.slug %} class="selected_tab" {%- endif -%}>{{ t.name }}</a>
{%- endfor %}
</div>
</div>
{% endif %}
<!--
{% block tests %}
{{ tests }}
{% endblock %}
-->
{% block script %}
<script src="{{ static('core/js/ui/jquery-ui.min.js') }}"></script>
<script src="{{ static('core/js/ui/i18n/datepicker-fr.js') }}"></script>
<script src="{{ static('core/js/jquery.datetimepicker.full.min.js') }}"></script>
<script src="{{ static('ajax_select/js/ajax_select.js') }}"></script>
<script src="{{ url('javascript-catalog') }}"></script>
<script>
function showMenu() {
let navbar = document.getElementById("navbar-content");
const current = navbar.style.getPropertyValue("display");
navbar.style.setProperty("display", current == "none" ? "block" : "none");
}
</script>
<script>
$('.select_date').datepicker({
changeMonth: true,
changeYear: true,
dayNamesShort: $.datepicker.regional[ "{{ request.LANGUAGE_CODE }}" ].dayNamesShort,
dayNames: $.datepicker.regional[ "{{ request.LANGUAGE_CODE }}" ].dayNames,
monthNamesShort: $.datepicker.regional[ "{{ request.LANGUAGE_CODE }}" ].monthNamesShort,
monthNames: $.datepicker.regional[ "{{ request.LANGUAGE_CODE }}" ].monthNames,
}).datepicker( $.datepicker.regional[ "{{ request.LANGUAGE_CODE }}"] );
$(document).keydown(function (e) {
if ($(e.target).is('input')) { return }
if ($(e.target).is('textarea')) { return }
if ($(e.target).is('select')) { return }
if (e.keyCode == 83) {
$("#search").focus();
return false;
}
});
jQuery.datetimepicker.setLocale('{{ request.LANGUAGE_CODE|lower }}');
$('.select_datetime').datetimepicker({
format: 'Y-m-d H:i:s',
});
</script>
{% if error %}
{{ error }}
{% endif %}
{% block content %}
{% endblock %}
</body>
</div>
</div>
{% if not popup %}
<footer>
{% block footer %}
<div>
<a href="{{ url('core:page', 'contacts') }}">{% trans %}Contacts{% endtrans %}</a>
<a href="{{ url('core:page', 'legals') }}">{% trans %}Legal notices{% endtrans %}</a>
<a href="{{ url('core:page', 'copyright_agent') }}">{% trans %}Intellectual property{% endtrans %}</a>
<a href="{{ url('core:page', 'docs') }}">{% trans %}Help & Documentation{% endtrans %}</a>
<a href="{{ url('core:page', 'rd') }}">{% trans %}R&D{% endtrans %}</a>
</div>
<a href="https://discord.gg/XK9WfPsUFm" target="_link">
{% trans %}Site created by the IT Department of the AE{% endtrans %}
</a>
{% endblock %}
<br>
<code class="version">
{% cache 1000 "sith_version" %}
{% trans %}Sith version:{% endtrans %}&nbsp;{{ get_sith().version }}
{% endcache %}
</code>
</footer>
{% endif %}
{% block script %}
<script src="{{ static('core/js/ui/jquery-ui.min.js') }}"></script>
<script src="{{ static('ajax_select/js/ajax_select.js') }}"></script>
<script src="{{ url('javascript-catalog') }}"></script>
<script>
function showMenu() {
let navbar = document.getElementById("navbar-content");
const current = navbar.style.getPropertyValue("display");
navbar.style.setProperty("display", current === "none" ? "block" : "none");
}
$(document).keydown(function (e) {
if ($(e.target).is('input')) { return }
if ($(e.target).is('textarea')) { return }
if ($(e.target).is('select')) { return }
if (e.keyCode === 83) {
$("#search").focus();
return false;
}
});
</script>
{% endblock %}
</body>
</html>

View File

@ -1,16 +1,16 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans name=form.instance.__class__._meta.verbose_name %}Create {{ name }}{% endtrans %}
{% trans name=form.instance.__class__._meta.verbose_name %}Create {{ name }}{% endtrans %}
{% endblock %}
{% block content %}
<h2>{% trans name=form.instance.__class__._meta.verbose_name %}Create {{ name }}{% endtrans %}</h2>
<form action="" method="post" enctype="multipart/form-data">
<h2>{% trans name=form.instance.__class__._meta.verbose_name %}Create {{ name }}{% endtrans %}</h2>
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p() }}
<p><input type="submit" value="{% trans %}Save{% endtrans %}" /></p>
</form>
</form>
{% endblock %}

View File

@ -1,7 +1,7 @@
{% extends "core/base.jinja" %}
{% block title %}
{% trans %}Delete confirmation{% endtrans %}
{% trans %}Delete confirmation{% endtrans %}
{% endblock %}
{% block info_boxes %}
@ -11,14 +11,14 @@
{% endblock %}
{% block content %}
<h2>{% trans %}Delete confirmation{% endtrans %}</h2>
<form action="" method="post">{% csrf_token %}
<h2>{% trans %}Delete confirmation{% endtrans %}</h2>
<form action="" method="post">{% csrf_token %}
<p>{% trans obj=object %}Are you sure you want to delete "{{ obj }}"?{% endtrans %}</p>
<input type="submit" value="{% trans %}Confirm{% endtrans %}" />
</form>
<form method="GET" action="javascript:history.back();">
</form>
<form method="GET" action="javascript:history.back();">
<input type="submit" name="cancel" value="{% trans %}Cancel{% endtrans %}" />
</form>
</form>
{% endblock %}

View File

@ -1,24 +1,24 @@
{% extends "core/base.jinja" %}
{% block title %}
{% if object %}
{% trans obj=object %}Edit {{ obj }}{% endtrans %}
{% else %}
{% trans %}Save{% endtrans %}
{% endif %}
{% if object %}
{% trans obj=object %}Edit {{ obj }}{% endtrans %}
{% else %}
{% trans %}Save{% endtrans %}
{% endif %}
{% endblock %}
{% block content %}
{% if object %}
<h2>{% trans obj=object %}Edit {{ obj }}{% endtrans %}</h2>
{% else %}
<h2>{% trans %}Save{% endtrans %}</h2>
{% endif %}
<form action="" method="post" enctype="multipart/form-data">
{% if object %}
<h2>{% trans obj=object %}Edit {{ obj }}{% endtrans %}</h2>
{% else %}
<h2>{% trans %}Save{% endtrans %}</h2>
{% endif %}
<form action="" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p() }}
<p><input type="submit" value="{% trans %}Save{% endtrans %}" /></p>
</form>
</form>
{% endblock %}

View File

@ -1,62 +1,62 @@
{% extends "core/base.jinja" %}
{% block title %}
{% if file %}
{{ file.get_display_name() }}
{% elif file_list %}
{% trans %}File list{% endtrans %}
{% elif new_file %}
{% trans %}New file{% endtrans %}
{% else %}
{% trans %}Not found{% endtrans %}
{% endif %}
{% if file %}
{{ file.get_display_name() }}
{% elif file_list %}
{% trans %}File list{% endtrans %}
{% elif new_file %}
{% trans %}New file{% endtrans %}
{% else %}
{% trans %}Not found{% endtrans %}
{% endif %}
{% endblock %}
{% macro print_file_name(file) %}
{% if file %}
{{ print_file_name(file.parent) }} >
<a href="{{ url('core:file_detail', file_id=file.id, popup=popup) }}">{{ file.get_display_name() }}</a>
{% else %}
<a href="{{ url('core:file_list', popup) }}">{% trans %}Files{% endtrans %}</a>
{% endif %}
{% if file %}
{{ print_file_name(file.parent) }} >
<a href="{{ url('core:file_detail', file_id=file.id, popup=popup) }}">{{ file.get_display_name() }}</a>
{% else %}
<a href="{{ url('core:file_list', popup) }}">{% trans %}Files{% endtrans %}</a>
{% endif %}
{% endmacro %}
{% block content %}
{{ print_file_name(file) }}
{{ print_file_name(file) }}
<div class="tool_bar">
<div class="tool_bar">
<div class="tools">
<div>
{% set home = user.home %}
{% if home %}
<a href="{{ url('core:file_detail', home.id, popup) }}">{% trans %}My files{% endtrans %}</a>
{% endif %}
</div>
{% if file %}
<div>
{% set home = user.home %}
{% if home %}
<a href="{{ url('core:file_detail', home.id, popup) }}">{% trans %}My files{% endtrans %}</a>
{% endif %}
</div>
{% if file %}
<a href="{{ url('core:file_detail', file.id, popup) }}">{% trans %}View{% endtrans %}</a>
{% if can_edit(file, user) %}
<a href="{{ url('core:file_edit', file_id=file.id, popup=popup) }}">{% trans %}Edit{% endtrans %}</a>
<a href="{{ url('core:file_edit', file_id=file.id, popup=popup) }}">{% trans %}Edit{% endtrans %}</a>
{% endif %}
{% if can_edit_prop(file, user) %}
<a href="{{ url('core:file_prop', file_id=file.id, popup=popup) }}">{% trans %}Prop{% endtrans %}</a>
{% endif %}
<a href="{{ url('core:file_prop', file_id=file.id, popup=popup) }}">{% trans %}Prop{% endtrans %}</a>
{% endif %}
{% endif %}
</div>
</div>
<hr>
</div>
<hr>
{% if file %}
{% block file %}
{% endblock %}
{% endif %}
{% if file %}
{% block file %}
{% endblock %}
{% endif %}
{% block script %}
{{ super() }}
{% if popup %}
<script>
parent.$(".choose_file_widget").css("height", "75%");
</script>
{% endif %}
{% endblock %}
{% block script %}
{{ super() }}
{% if popup %}
<script>
parent.$(".choose_file_widget").css("height", "75%");
</script>
{% endif %}
{% endblock %}
{% endblock %}

Some files were not shown because too many files have changed in this diff Show More