Compare commits
179 Commits
moderation
...
main
Author | SHA1 | Date | |
---|---|---|---|
|
430c7b47a2 | ||
|
63d3cb76ea | ||
|
d770cf23f0 | ||
|
cc0c798f5a | ||
|
6ceec954d8 | ||
|
2c22d62302 | ||
|
f79b1f0f89 | ||
|
3c1d51dda1 | ||
|
141949991c | ||
|
290faf0b8f | ||
|
f9f690cac7 | ||
|
5e8d9766ee | ||
|
6589c1b0c3 | ||
|
1055a36084 | ||
|
2cca0322d1 | ||
|
e5c075656c | ||
|
5ef9358b28 | ||
|
0526854d6b | ||
|
bc06b6205d | ||
|
504198b14f | ||
|
526b83ec20 | ||
|
08e66918ab | ||
|
c34fb666b2 | ||
|
a94b9a53f2 | ||
|
c1f7bfd8c4 | ||
|
b1e5414519 | ||
|
3da9a5239a | ||
|
c91cdf0c99 | ||
|
6e8f00ccbe | ||
|
a1984f60f5 | ||
|
ce95fe6504 | ||
|
dd0c037929 | ||
|
41d6b39988 | ||
|
3316d28e09 | ||
|
f7f8d9cb0c | ||
|
ced15d5113 | ||
|
70ae92854f | ||
|
02448cf4d4 | ||
|
14e25b660c | ||
|
92da6585c6 | ||
|
cd52ae0286 | ||
|
e050ce5eda | ||
|
b0b828392a | ||
|
c34abe9158 | ||
|
f52caf9855 | ||
|
bd1330cd2f | ||
|
a31bcc2764 | ||
|
91907be984 | ||
|
27ceac1e46 | ||
|
b3cba9293c | ||
|
c857294345 | ||
|
5a7cc080c7 | ||
|
37ed7c45db | ||
|
bda14c6ccb | ||
|
3d70de9c1b | ||
|
874c1779f8 | ||
|
084b3dfb25 | ||
|
ec707bf272 | ||
|
21b42e4fee | ||
|
d55d029fc7 | ||
|
1d9251946c | ||
|
e875ae626b | ||
|
63aad60260 | ||
|
27bce22670 | ||
|
1fc1fc13e1 | ||
|
252fb8c27b | ||
|
d70eca6493 | ||
|
7f1bbabebf | ||
|
c55ed5c4dc | ||
|
ac3d6796cf | ||
|
bf773686f9 | ||
|
1256adcb8a | ||
|
7120da3e28 | ||
|
4e9ac573ac | ||
|
42fb85af48 | ||
|
256fed1e2e | ||
|
d46ebeae3b | ||
|
3be7d901c8 | ||
|
5549d2172c | ||
|
674bba4a98 | ||
|
34008625d2 | ||
|
65430a2a8f | ||
|
8ef620c8e1 | ||
|
d119f1fa45 | ||
|
41f6dbc352 | ||
ce602c10bd | |||
|
c9275c5ea0 | ||
|
1287d9ee06 | ||
|
d7ec80ff01 | ||
|
555bae8dc8 | ||
|
ac8ddc5123 | ||
|
f9678bbf81 | ||
2680622dfc | |||
|
3c5b5a9fd6 | ||
|
db7604623c | ||
|
afa1844d21 | ||
|
b0b653c1b1 | ||
|
3001685937 | ||
|
a3e13429eb | ||
|
ea5372cae5 | ||
|
5e65ecdb5c | ||
|
ed7944aaa9 | ||
|
5a2dea6989 | ||
|
98092de1f0 | ||
|
03e10e91e2 | ||
|
7a9e74b057 | ||
|
0872af5144 | ||
|
720a187116 | ||
|
3d8fd1cfdf | ||
|
524d178055 | ||
|
2da854545f | ||
|
5a66caae55 | ||
|
ecc347219c | ||
|
918e19fa4f | ||
|
70260fcb4f | ||
|
0190d91268 | ||
|
769c607550 | ||
|
7f029ae541 | ||
|
386eca261a | ||
|
37817cc8f5 | ||
|
96401b6519 | ||
|
4e1441a92f | ||
|
b569464894 | ||
|
507670ebde | ||
|
c5c68bcfef | ||
|
d39ea43efb | ||
|
11bd53cbeb | ||
|
4cc6db84e2 | ||
|
463dd6b3b9 | ||
|
09c2c2117c | ||
|
c4bb86dab4 | ||
|
283ffc4348 | ||
|
2a0abf8e5a | ||
|
62b73dd836 | ||
|
1e278581ed | ||
|
0924d5d36c | ||
|
be62272487 | ||
|
bf5db35e57 | ||
|
af2948827d | ||
|
182208a6f8 | ||
|
9ad3e9e972 | ||
|
fe97c4cb32 | ||
|
956ec7210c | ||
|
5a6f33f8e2 | ||
|
c3f6d6920e | ||
|
47aedc706b | ||
|
1e9698da91 | ||
|
4a0f5b3b14 | ||
|
33a68ee7eb | ||
|
9cab07cb6f | ||
|
6efd6f18c8 | ||
|
493b42c457 | ||
|
0be3c30489 | ||
|
44a04deb26 | ||
|
43e1d3fd26 | ||
|
ae542f76c8 | ||
|
5cfb53de23 | ||
|
11d5cf9aa4 | ||
|
e3c14437ac | ||
|
28f5b2a01b | ||
|
0263976573 | ||
|
79a73d6459 | ||
|
3bd4ef5771 | ||
|
637b976442 | ||
|
d47991d1e0 | ||
|
350a555bea | ||
|
dbf62f3b4a | ||
|
4af14c523c | ||
|
0ae9c399dd | ||
|
e767babd8e | ||
|
df27949036 | ||
|
1f60bf0c39 | ||
|
bb6d83f6fb | ||
|
f93a6164ca | ||
|
fe1061e638 | ||
|
ed2f530f0c | ||
|
0bdd8693ec | ||
|
2ce8f30275 | ||
|
4c2dd9e98c |
@ -36,7 +36,7 @@ Pour ajouter une nouvelle source custom:
|
||||
### Récupérer un dump du prod sur un serveur dev
|
||||
|
||||
* sur le serveur de dev:
|
||||
* ```docker exec -i agenda_culturel-backend python3 manage.py dumpdata --format=json --exclude=admin.logentry --exclude=auth.group --exclude=auth.permission --exclude=auth.user --exclude=contenttypes --indent=2 > fixtures/postgres-backup-20241101.json``` (à noter qu'ici on oublie les comptes, qu'il faudra recréer)
|
||||
* ```docker exec -i agenda_culturel-backend python3 manage.py dumpdata --natural-foreign --natural-primary --format=json --exclude=admin.logentry --indent=2 > fixtures/postgres-backup-20241101.json``` (à noter qu'ici on oublie les comptes, qu'il faudra recréer)
|
||||
* sur le serveur de prod:
|
||||
* On récupère le dump json ```scp $SERVEUR:$PATH/fixtures/postgres-backup-20241101.json src/fixtures/```
|
||||
* ```scripts/reset-database.sh FIXTURE COMMIT``` où ```FIXTURE``` est le timestamp dans le nom de la fixture, et ```COMMIT``` est l'ID du commit git correspondant à celle en prod sur le serveur au moment de la création de la fixture
|
||||
|
@ -5,10 +5,11 @@ WORKDIR /usr/src/app
|
||||
|
||||
RUN --mount=type=cache,target=/var/cache/apt \
|
||||
apt-get update && \
|
||||
apt-get install --no-install-recommends -y build-essential libpq-dev gettext chromium-driver gdal-bin \
|
||||
apt-get install --no-install-recommends -y build-essential libpq-dev gettext chromium-driver gdal-bin fonts-symbola \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
|
||||
|
||||
COPY src/requirements.txt ./requirements.txt
|
||||
|
||||
RUN --mount=type=cache,target=/root/.cache/pip \
|
||||
|
@ -32,7 +32,7 @@ http {
|
||||
error_page 502 /static/html/500.html;
|
||||
error_page 503 /static/html/500.html;
|
||||
|
||||
if ($http_user_agent ~* "(?:Amazonbot)") {
|
||||
if ($http_user_agent ~* (Amazonbot|meta-externalagent|ClaudeBot)) {
|
||||
return 444;
|
||||
}
|
||||
|
||||
|
43
experimentations/get_le_rio.py
Executable file
43
experimentations/get_le_rio.py
Executable file
@ -0,0 +1,43 @@
|
||||
#!/usr/bin/python3
|
||||
# coding: utf-8
|
||||
|
||||
import os
|
||||
import json
|
||||
import sys
|
||||
|
||||
# getting the name of the directory
|
||||
# where the this file is present.
|
||||
current = os.path.dirname(os.path.realpath(__file__))
|
||||
|
||||
# Getting the parent directory name
|
||||
# where the current directory is present.
|
||||
parent = os.path.dirname(current)
|
||||
|
||||
# adding the parent directory to
|
||||
# the sys.path.
|
||||
sys.path.append(parent)
|
||||
|
||||
from src.agenda_culturel.import_tasks.downloader import *
|
||||
from src.agenda_culturel.import_tasks.extractor import *
|
||||
from src.agenda_culturel.import_tasks.importer import *
|
||||
from src.agenda_culturel.import_tasks.custom_extractors import *
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
u2e = URL2Events(SimpleDownloader(), lerio.CExtractor())
|
||||
url = "https://www.cinemalerio.com/evenements/"
|
||||
url_human = "https://www.cinemalerio.com/evenements/"
|
||||
|
||||
try:
|
||||
events = u2e.process(url, url_human, cache = "cache-le-rio.html", default_values = {"location": "Cinéma le Rio", "category": "Cinéma"}, published = True)
|
||||
|
||||
exportfile = "events-le-roi.json"
|
||||
print("Saving events to file {}".format(exportfile))
|
||||
with open(exportfile, "w") as f:
|
||||
json.dump(events, f, indent=4, default=str)
|
||||
except Exception as e:
|
||||
print("Exception: " + str(e))
|
@ -73,6 +73,10 @@ git checkout $COMMIT
|
||||
echobold "Setup database stucture according to the selected commit"
|
||||
docker exec -i agenda_culturel-backend python3 manage.py migrate agenda_culturel
|
||||
|
||||
# remove all elements in database
|
||||
echobold "Flush database"
|
||||
docker exec -i agenda_culturel-backend python3 manage.py flush --no-input
|
||||
|
||||
# import data
|
||||
echobold "Import data"
|
||||
docker exec -i agenda_culturel-backend python3 manage.py loaddata --format=json $FFILE
|
||||
@ -85,7 +89,4 @@ git checkout main
|
||||
echobold "Update database"
|
||||
docker exec -i agenda_culturel-backend python3 manage.py migrate agenda_culturel
|
||||
|
||||
# create superuser
|
||||
echobold "Create superuser"
|
||||
docker exec -ti agenda_culturel-backend python3 manage.py createsuperuser
|
||||
|
||||
|
@ -9,8 +9,9 @@ from .models import (
|
||||
BatchImportation,
|
||||
RecurrentImport,
|
||||
Place,
|
||||
ContactMessage,
|
||||
ReferenceLocation
|
||||
Message,
|
||||
ReferenceLocation,
|
||||
Organisation
|
||||
)
|
||||
from django_better_admin_arrayfield.admin.mixins import DynamicArrayMixin
|
||||
from django_better_admin_arrayfield.forms.widgets import DynamicArrayWidget
|
||||
@ -24,8 +25,9 @@ admin.site.register(DuplicatedEvents)
|
||||
admin.site.register(BatchImportation)
|
||||
admin.site.register(RecurrentImport)
|
||||
admin.site.register(Place)
|
||||
admin.site.register(ContactMessage)
|
||||
admin.site.register(Message)
|
||||
admin.site.register(ReferenceLocation)
|
||||
admin.site.register(Organisation)
|
||||
|
||||
|
||||
class URLWidget(DynamicArrayWidget):
|
||||
|
@ -117,6 +117,23 @@ class DayInCalendar:
|
||||
if e.start_time is None
|
||||
else e.start_time
|
||||
)
|
||||
self.today_night = False
|
||||
if self.is_today():
|
||||
self.today_night = True
|
||||
now = timezone.now()
|
||||
nday = now.date()
|
||||
ntime = now.time()
|
||||
found = False
|
||||
for idx,e in enumerate(self.events):
|
||||
if (nday < e.start_day) or (nday == e.start_day and e.start_time and ntime <= e.start_time):
|
||||
self.events[idx].is_first_after_now = True
|
||||
found = True
|
||||
break
|
||||
if found:
|
||||
self.today_night = False
|
||||
|
||||
def is_today_after_events(self):
|
||||
return self.is_today() and self.today_night
|
||||
|
||||
def events_by_category_ordered(self):
|
||||
from .models import Category
|
||||
@ -175,12 +192,13 @@ class IntervalInDay(DayInCalendar):
|
||||
self.id = self.id + '-' + str(id)
|
||||
|
||||
class CalendarList:
|
||||
def __init__(self, firstdate, lastdate, filter=None, exact=False, ignore_dup=None):
|
||||
def __init__(self, firstdate, lastdate, filter=None, exact=False, ignore_dup=None, qs=None):
|
||||
self.firstdate = firstdate
|
||||
self.lastdate = lastdate
|
||||
self.now = date.today()
|
||||
self.filter = filter
|
||||
self.ignore_dup = ignore_dup
|
||||
self.qs = qs
|
||||
|
||||
if exact:
|
||||
self.c_firstdate = self.firstdate
|
||||
@ -219,9 +237,12 @@ class CalendarList:
|
||||
|
||||
def fill_calendar_days(self):
|
||||
if self.filter is None:
|
||||
from .models import Event
|
||||
if self.qs is None:
|
||||
from .models import Event
|
||||
|
||||
qs = Event.objects.all()
|
||||
qs = Event.objects.all()
|
||||
else:
|
||||
qs = self.qs
|
||||
else:
|
||||
qs = self.filter.qs
|
||||
|
||||
@ -229,7 +250,7 @@ class CalendarList:
|
||||
qs = qs.exclude(other_versions=self.ignore_dup)
|
||||
startdatetime = timezone.make_aware(datetime.combine(self.c_firstdate, time.min), timezone.get_default_timezone())
|
||||
lastdatetime = timezone.make_aware(datetime.combine(self.c_lastdate, time.max), timezone.get_default_timezone())
|
||||
self.events = qs.filter(
|
||||
qs = qs.filter(
|
||||
(Q(recurrence_dtend__isnull=True) & Q(recurrence_dtstart__lte=lastdatetime))
|
||||
| (
|
||||
Q(recurrence_dtend__isnull=False)
|
||||
@ -238,11 +259,15 @@ class CalendarList:
|
||||
| Q(recurrence_dtend__lt=startdatetime)
|
||||
)
|
||||
)
|
||||
| (Q(start_day__lte=self.c_firstdate) & (Q(end_day__isnull=True) | Q(end_day__gte=self.c_firstdate)))
|
||||
).filter(
|
||||
Q(other_versions__isnull=True) |
|
||||
Q(other_versions__representative=F('pk')) |
|
||||
Q(other_versions__representative__isnull=True)
|
||||
).order_by("start_time", "title__unaccent__lower").prefetch_related("exact_location").prefetch_related("category").prefetch_related("other_versions")
|
||||
).order_by("start_time", "title__unaccent__lower")
|
||||
|
||||
qs = qs.select_related("exact_location").select_related("category").select_related("other_versions").select_related("other_versions__representative")
|
||||
self.events = qs
|
||||
|
||||
firstdate = datetime.fromordinal(self.c_firstdate.toordinal())
|
||||
if firstdate.tzinfo is None or firstdate.tzinfo.utcoffset(firstdate) is None:
|
||||
@ -290,14 +315,14 @@ class CalendarList:
|
||||
def time_intervals_list_first(self):
|
||||
return self.time_intervals_list(True)
|
||||
|
||||
def export_to_ics(self):
|
||||
def export_to_ics(self, request):
|
||||
from .models import Event
|
||||
events = [event for day in self.get_calendar_days().values() for event in day.events]
|
||||
return Event.export_to_ics(events)
|
||||
return Event.export_to_ics(events, request)
|
||||
|
||||
|
||||
class CalendarMonth(CalendarList):
|
||||
def __init__(self, year, month, filter):
|
||||
def __init__(self, year, month, filter, qs=None):
|
||||
self.year = year
|
||||
self.month = month
|
||||
r = calendar.monthrange(year, month)
|
||||
@ -305,7 +330,7 @@ class CalendarMonth(CalendarList):
|
||||
first = date(year, month, 1)
|
||||
last = date(year, month, r[1])
|
||||
|
||||
super().__init__(first, last, filter)
|
||||
super().__init__(first, last, filter, qs)
|
||||
|
||||
def get_month_name(self):
|
||||
return self.firstdate.strftime("%B")
|
||||
@ -318,14 +343,14 @@ class CalendarMonth(CalendarList):
|
||||
|
||||
|
||||
class CalendarWeek(CalendarList):
|
||||
def __init__(self, year, week, filter):
|
||||
def __init__(self, year, week, filter, qs=None):
|
||||
self.year = year
|
||||
self.week = week
|
||||
|
||||
first = date.fromisocalendar(self.year, self.week, 1)
|
||||
last = date.fromisocalendar(self.year, self.week, 7)
|
||||
|
||||
super().__init__(first, last, filter)
|
||||
super().__init__(first, last, filter, qs)
|
||||
|
||||
def next_week(self):
|
||||
return self.firstdate + timedelta(days=7)
|
||||
@ -335,8 +360,8 @@ class CalendarWeek(CalendarList):
|
||||
|
||||
|
||||
class CalendarDay(CalendarList):
|
||||
def __init__(self, date, filter=None):
|
||||
super().__init__(date, date, filter, exact=True)
|
||||
def __init__(self, date, filter=None, qs=None):
|
||||
super().__init__(date, date, filter=filter, qs=qs, exact=True)
|
||||
|
||||
def get_events(self):
|
||||
return self.calendar_days_list()[0].events
|
||||
|
@ -6,7 +6,8 @@ from celery.schedules import crontab
|
||||
from celery.utils.log import get_task_logger
|
||||
from celery.exceptions import MaxRetriesExceededError
|
||||
import time as time_
|
||||
|
||||
from django.conf import settings
|
||||
from celery.signals import worker_ready
|
||||
|
||||
from contextlib import contextmanager
|
||||
|
||||
@ -147,6 +148,8 @@ def run_recurrent_import_internal(rimport, downloader, req_id):
|
||||
extractor = c3c.CExtractor()
|
||||
elif rimport.processor == RecurrentImport.PROCESSOR.ARACHNEE:
|
||||
extractor = arachnee.CExtractor()
|
||||
elif rimport.processor == RecurrentImport.PROCESSOR.LERIO:
|
||||
extractor = lerio.CExtractor()
|
||||
else:
|
||||
extractor = None
|
||||
|
||||
@ -162,13 +165,14 @@ def run_recurrent_import_internal(rimport, downloader, req_id):
|
||||
location = rimport.defaultLocation
|
||||
tags = rimport.defaultTags
|
||||
published = rimport.defaultPublished
|
||||
organisers = [] if rimport.defaultOrganiser is None else [rimport.defaultOrganiser.pk]
|
||||
|
||||
try:
|
||||
# get events from website
|
||||
events = u2e.process(
|
||||
url,
|
||||
browsable_url,
|
||||
default_values={"category": category, "location": location, "tags": tags},
|
||||
default_values={"category": category, "location": location, "tags": tags, "organisers": organisers},
|
||||
published=published,
|
||||
)
|
||||
|
||||
@ -247,6 +251,23 @@ def daily_imports(self):
|
||||
run_recurrent_imports_from_list([imp.pk for imp in imports])
|
||||
|
||||
|
||||
SCREENSHOT_FILE = settings.MEDIA_ROOT + '/screenshot.png'
|
||||
|
||||
@app.task(bind=True)
|
||||
def screenshot(self):
|
||||
downloader = ChromiumHeadlessDownloader(noimage=False)
|
||||
downloader.screenshot("https://pommesdelune.fr", SCREENSHOT_FILE)
|
||||
|
||||
@worker_ready.connect
|
||||
def at_start(sender, **k):
|
||||
if not os.path.isfile(SCREENSHOT_FILE):
|
||||
logger.info("Init screenshot file")
|
||||
with sender.app.connection() as conn:
|
||||
sender.app.send_task('agenda_culturel.celery.screenshot', None, connection=conn)
|
||||
else:
|
||||
logger.info("Screenshot file already exists")
|
||||
|
||||
|
||||
@app.task(bind=True)
|
||||
def run_all_recurrent_imports(self):
|
||||
from agenda_culturel.models import RecurrentImport
|
||||
@ -288,7 +309,7 @@ def weekly_imports(self):
|
||||
run_recurrent_imports_from_list([imp.pk for imp in imports])
|
||||
|
||||
@app.task(base=ChromiumTask, bind=True)
|
||||
def import_events_from_url(self, url, cat, force=False):
|
||||
def import_events_from_url(self, url, cat, tags, force=False, user_id=None):
|
||||
from .db_importer import DBImporterEvents
|
||||
from agenda_culturel.models import RecurrentImport, BatchImportation
|
||||
from agenda_culturel.models import Event, Category
|
||||
@ -322,7 +343,7 @@ def import_events_from_url(self, url, cat, force=False):
|
||||
# set default values
|
||||
values = {}
|
||||
if cat is not None:
|
||||
values = {"category": cat}
|
||||
values = {"category": cat, "tags": tags}
|
||||
|
||||
# get event
|
||||
events = u2e.process(
|
||||
@ -334,7 +355,7 @@ def import_events_from_url(self, url, cat, force=False):
|
||||
json_events = json.dumps(events, default=str)
|
||||
|
||||
# import events (from json)
|
||||
success, error_message = importer.import_events(json_events)
|
||||
success, error_message = importer.import_events(json_events, user_id)
|
||||
|
||||
# finally, close task
|
||||
close_import_task(self.request.id, success, error_message, importer)
|
||||
@ -351,13 +372,14 @@ def import_events_from_url(self, url, cat, force=False):
|
||||
|
||||
|
||||
@app.task(base=ChromiumTask, bind=True)
|
||||
def import_events_from_urls(self, urls_and_cats):
|
||||
for ucat in urls_and_cats:
|
||||
def import_events_from_urls(self, urls_cat_tags, user_id=None):
|
||||
for ucat in urls_cat_tags:
|
||||
if ucat is not None:
|
||||
url = ucat[0]
|
||||
cat = ucat[1]
|
||||
tags = ucat[2]
|
||||
|
||||
import_events_from_url.delay(url, cat)
|
||||
import_events_from_url.delay(url, cat, tags, user_id=user_id)
|
||||
|
||||
|
||||
app.conf.beat_schedule = {
|
||||
@ -366,6 +388,10 @@ app.conf.beat_schedule = {
|
||||
# Daily imports at 3:14 a.m.
|
||||
"schedule": crontab(hour=3, minute=14),
|
||||
},
|
||||
"daily_screenshot": {
|
||||
"task": "agenda_culturel.celery.screenshot",
|
||||
"schedule": crontab(hour=3, minute=3),
|
||||
},
|
||||
"weekly_imports": {
|
||||
"task": "agenda_culturel.celery.weekly_imports",
|
||||
# Daily imports on Mondays at 2:22 a.m.
|
||||
|
@ -11,6 +11,7 @@ class DBImporterEvents:
|
||||
def __init__(self, celery_id):
|
||||
self.celery_id = celery_id
|
||||
self.error_message = ""
|
||||
self.user_id = None
|
||||
self.init_result_properties()
|
||||
self.today = timezone.now().date().isoformat()
|
||||
|
||||
@ -34,9 +35,10 @@ class DBImporterEvents:
|
||||
def get_nb_removed_events(self):
|
||||
return self.nb_removed
|
||||
|
||||
def import_events(self, json_structure):
|
||||
def import_events(self, json_structure, user_id=None):
|
||||
print(json_structure)
|
||||
self.init_result_properties()
|
||||
self.user_id = user_id
|
||||
|
||||
try:
|
||||
structure = json.loads(json_structure)
|
||||
@ -95,7 +97,7 @@ class DBImporterEvents:
|
||||
|
||||
def save_imported(self):
|
||||
self.db_event_objects, self.nb_updated, self.nb_removed = Event.import_events(
|
||||
self.event_objects, remove_missing_from_source=self.url
|
||||
self.event_objects, remove_missing_from_source=self.url, user_id=self.user_id
|
||||
)
|
||||
|
||||
def is_valid_event_structure(self, event):
|
||||
|
505
src/agenda_culturel/filters.py
Normal file
505
src/agenda_culturel/filters.py
Normal file
@ -0,0 +1,505 @@
|
||||
import django_filters
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django import forms
|
||||
from django.contrib.postgres.search import SearchQuery, SearchHeadline
|
||||
from django.db.models import Count, Q
|
||||
|
||||
from django.http import QueryDict
|
||||
from django.contrib.gis.measure import D
|
||||
|
||||
from django.forms import (
|
||||
ModelForm,
|
||||
ValidationError,
|
||||
TextInput,
|
||||
Form,
|
||||
URLField,
|
||||
MultipleHiddenInput,
|
||||
Textarea,
|
||||
CharField,
|
||||
ChoiceField,
|
||||
RadioSelect,
|
||||
MultipleChoiceField,
|
||||
BooleanField,
|
||||
HiddenInput,
|
||||
ModelChoiceField,
|
||||
)
|
||||
|
||||
from .forms import (
|
||||
URLSubmissionForm,
|
||||
EventForm,
|
||||
BatchImportationForm,
|
||||
FixDuplicates,
|
||||
SelectEventInList,
|
||||
MergeDuplicates,
|
||||
RecurrentImportForm,
|
||||
CategorisationRuleImportForm,
|
||||
CategorisationForm,
|
||||
EventAddPlaceForm,
|
||||
PlaceForm,
|
||||
)
|
||||
|
||||
from .models import (
|
||||
ReferenceLocation,
|
||||
RecurrentImport,
|
||||
Tag,
|
||||
Event,
|
||||
Category,
|
||||
Message,
|
||||
DuplicatedEvents
|
||||
)
|
||||
|
||||
|
||||
class EventFilter(django_filters.FilterSet):
|
||||
RECURRENT_CHOICES = [
|
||||
("remove_recurrent", "Masquer les événements récurrents"),
|
||||
("only_recurrent", "Montrer uniquement les événements récurrents"),
|
||||
]
|
||||
|
||||
DISTANCE_CHOICES = [5, 10, 15, 30]
|
||||
|
||||
position = django_filters.ModelChoiceFilter(
|
||||
label="À proximité de",
|
||||
method="no_filter",
|
||||
empty_label=_("Select a location"),
|
||||
queryset=ReferenceLocation.objects.all().order_by("-main", "name__unaccent")
|
||||
)
|
||||
|
||||
radius = django_filters.ChoiceFilter(
|
||||
label="Dans un rayon de",
|
||||
method="no_filter",
|
||||
choices=[(x, str(x) + " km") for x in DISTANCE_CHOICES],
|
||||
null_label=None,
|
||||
empty_label=None
|
||||
)
|
||||
|
||||
exclude_tags = django_filters.MultipleChoiceFilter(
|
||||
label="Exclure les étiquettes",
|
||||
choices=[],
|
||||
lookup_expr="icontains",
|
||||
field_name="tags",
|
||||
exclude=True,
|
||||
widget=forms.SelectMultiple,
|
||||
)
|
||||
|
||||
tags = django_filters.MultipleChoiceFilter(
|
||||
label="Inclure les étiquettes",
|
||||
choices=[],
|
||||
lookup_expr="icontains",
|
||||
conjoined=True,
|
||||
field_name="tags",
|
||||
widget=forms.SelectMultiple,
|
||||
)
|
||||
|
||||
recurrences = django_filters.ChoiceFilter(
|
||||
label="Inclure la récurrence",
|
||||
choices=RECURRENT_CHOICES,
|
||||
method="filter_recurrences",
|
||||
)
|
||||
|
||||
category = django_filters.ModelMultipleChoiceFilter(
|
||||
label="Filtrer par catégories",
|
||||
field_name="category__id",
|
||||
to_field_name="id",
|
||||
queryset=Category.objects.all(),
|
||||
widget=MultipleHiddenInput,
|
||||
)
|
||||
|
||||
|
||||
status = django_filters.MultipleChoiceFilter(
|
||||
label="Filtrer par status",
|
||||
choices=Event.STATUS.choices,
|
||||
field_name="status",
|
||||
widget=forms.CheckboxSelectMultiple,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Event
|
||||
fields = ["category", "tags", "exclude_tags", "status", "recurrences"]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
if not kwargs["request"].user.is_authenticated:
|
||||
self.form.fields.pop("status")
|
||||
self.form.fields["exclude_tags"].choices = Tag.get_tag_groups(exclude=True, nb_suggestions=0)
|
||||
self.form.fields["tags"].choices = Tag.get_tag_groups(include=True)
|
||||
|
||||
def filter_recurrences(self, queryset, name, value):
|
||||
# construct the full lookup expression
|
||||
lookup = "__".join([name, "isnull"])
|
||||
return queryset.filter(**{lookup: value == "remove_recurrent"})
|
||||
|
||||
def no_filter(self, queryset, name, value):
|
||||
return queryset
|
||||
|
||||
@property
|
||||
def qs(self):
|
||||
parent = super().qs
|
||||
if self.get_cleaned_data("position") is None or self.get_cleaned_data("radius") is None:
|
||||
return parent
|
||||
d = self.get_cleaned_data("radius")
|
||||
p = self.get_cleaned_data("position")
|
||||
if not isinstance(d, str) or not isinstance(p, ReferenceLocation):
|
||||
return parent
|
||||
p = p.location
|
||||
|
||||
return parent.exclude(exact_location=False).filter(exact_location__location__distance_lt=(p, D(km=d)))
|
||||
|
||||
def get_url(self):
|
||||
if isinstance(self.form.data, QueryDict):
|
||||
return self.form.data.urlencode()
|
||||
else:
|
||||
return ""
|
||||
|
||||
def get_full_url(self):
|
||||
return self.request.get_full_path()
|
||||
|
||||
def get_url_remove_categories(self, catpks, full_path = None):
|
||||
if full_path is None:
|
||||
full_path = self.request.get_full_path()
|
||||
|
||||
result = full_path
|
||||
for catpk in catpks:
|
||||
result = result.replace('category=' + str(catpk), '')
|
||||
result = result.replace('?&', '?')
|
||||
result = result.replace('&&', '&')
|
||||
return result
|
||||
|
||||
def get_url_add_categories(self, catpks, full_path = None):
|
||||
if full_path is None:
|
||||
full_path = self.request.get_full_path()
|
||||
|
||||
result = full_path
|
||||
for catpk in catpks:
|
||||
result = result + ('&' if '?' in full_path else '?') + 'category=' + str(catpk)
|
||||
return result
|
||||
|
||||
def get_url_without_filters_only_cats(self):
|
||||
return self.get_url_without_filters(True)
|
||||
|
||||
|
||||
def get_url_without_filters(self, only_categories=False):
|
||||
|
||||
if only_categories:
|
||||
# on repart d'une url sans option
|
||||
result = self.request.get_full_path().split("?")[0]
|
||||
# on ajoute toutes les catégories
|
||||
result = self.get_url_add_categories([c.pk for c in self.get_categories()], result)
|
||||
else:
|
||||
# on supprime toutes les catégories
|
||||
result = self.get_url_remove_categories([c.pk for c in self.get_categories()])
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def get_cleaned_data(self, name):
|
||||
|
||||
try:
|
||||
return self.form.cleaned_data[name]
|
||||
except AttributeError:
|
||||
return {}
|
||||
except KeyError:
|
||||
return {}
|
||||
|
||||
def get_categories(self):
|
||||
return self.get_cleaned_data("category")
|
||||
|
||||
def has_category(self):
|
||||
return "category" in self.form.cleaned_data and len(self.get_cleaned_data("category")) > 0
|
||||
|
||||
def get_tags(self):
|
||||
return self.get_cleaned_data("tags")
|
||||
|
||||
def get_exclude_tags(self):
|
||||
return self.get_cleaned_data("exclude_tags")
|
||||
|
||||
def get_status(self):
|
||||
return self.get_cleaned_data("status")
|
||||
|
||||
def get_position(self):
|
||||
return self.get_cleaned_data("position")
|
||||
|
||||
def get_radius(self):
|
||||
return self.get_cleaned_data("radius")
|
||||
|
||||
def to_str(self, prefix=''):
|
||||
self.form.full_clean()
|
||||
result = ' '.join([c.name for c in self.get_categories()] + [t for t in self.get_tags()] + ["~" + t for t in self.get_exclude_tags()] + [str(self.get_position()), str(self.get_radius())])
|
||||
if len(result) > 0:
|
||||
result = prefix + result
|
||||
return result
|
||||
|
||||
def get_status_names(self):
|
||||
if "status" in self.form.cleaned_data:
|
||||
return [
|
||||
dict(Event.STATUS.choices)[s] for s in self.get_cleaned_data("status")
|
||||
]
|
||||
else:
|
||||
return []
|
||||
|
||||
def get_recurrence_filtering(self):
|
||||
if "recurrences" in self.form.cleaned_data:
|
||||
d = dict(self.RECURRENT_CHOICES)
|
||||
v = self.form.cleaned_data["recurrences"]
|
||||
if v in d:
|
||||
return d[v]
|
||||
else:
|
||||
return ""
|
||||
else:
|
||||
return ""
|
||||
|
||||
|
||||
def is_resetable(self, only_categories=False):
|
||||
if only_categories:
|
||||
return len(self.get_cleaned_data("category")) != 0
|
||||
else:
|
||||
if self.request.user.is_authenticated:
|
||||
if (
|
||||
len(self.get_cleaned_data("status")) != 1
|
||||
or
|
||||
self.get_cleaned_data("status")[0] != Event.STATUS.PUBLISHED
|
||||
):
|
||||
return True
|
||||
else:
|
||||
if (
|
||||
len(self.get_cleaned_data("status")) != 0
|
||||
):
|
||||
return True
|
||||
return (
|
||||
len(self.get_cleaned_data("tags")) != 0
|
||||
or len(self.get_cleaned_data("exclude_tags")) != 0
|
||||
or len(self.get_cleaned_data("recurrences")) != 0
|
||||
or ((not self.get_cleaned_data("position") is None) and (not self.get_cleaned_data("radius") is None))
|
||||
)
|
||||
|
||||
def is_active(self, only_categories=False):
|
||||
if only_categories:
|
||||
return len(self.get_cleaned_data("category")) != 0
|
||||
else:
|
||||
return (
|
||||
len(self.get_cleaned_data("status")) != 0
|
||||
or len(self.get_cleaned_data("tags")) != 0
|
||||
or len(self.get_cleaned_data("exclude_tags")) != 0
|
||||
or len(self.get_cleaned_data("recurrences")) != 0
|
||||
or ((not self.get_cleaned_data("position") is None) and (not self.get_cleaned_data("radius") is None))
|
||||
)
|
||||
|
||||
def is_selected(self, cat):
|
||||
return "category" in self.form.cleaned_data and cat in self.form.cleaned_data["category"]
|
||||
|
||||
def is_selected_tag(self, tag):
|
||||
return "tags" in self.form.cleaned_data and tag in self.form.cleaned_data["tags"]
|
||||
|
||||
def get_url_add_tag(self, tag):
|
||||
full_path = self.request.get_full_path()
|
||||
|
||||
result = full_path + ('&' if '?' in full_path else '?') + 'tags=' + str(tag)
|
||||
|
||||
return result
|
||||
|
||||
def tag_exists(self, tag):
|
||||
return tag in [t[0] for g in self.form.fields["tags"].choices for t in g[1]]
|
||||
|
||||
def set_default_values(request):
|
||||
if request.user.is_authenticated:
|
||||
if request.GET.get('status', None) == None:
|
||||
tempdict = request.GET.copy()
|
||||
tempdict['status'] = 'published'
|
||||
request.GET = tempdict
|
||||
return request
|
||||
return request
|
||||
|
||||
def get_position_radius(self):
|
||||
if self.get_cleaned_data("position") is None or self.get_cleaned_data("radius") is None:
|
||||
return ""
|
||||
else:
|
||||
return str(self.get_cleaned_data("position")) + ' (' + str(self.get_cleaned_data("radius")) + ' km)'
|
||||
|
||||
def is_filtered_by_position_radius(self):
|
||||
return not self.get_cleaned_data("position") is None and not self.get_cleaned_data("radius") is None
|
||||
|
||||
def get_url_add_suggested_position(self, location):
|
||||
result = self.request.get_full_path()
|
||||
return result + ('&' if '?' in result else '?') + 'position=' + str(location.pk) + "&radius=" + str(location.suggested_distance)
|
||||
|
||||
|
||||
class EventFilterAdmin(django_filters.FilterSet):
|
||||
status = django_filters.MultipleChoiceFilter(
|
||||
choices=Event.STATUS.choices, widget=forms.CheckboxSelectMultiple
|
||||
)
|
||||
|
||||
representative = django_filters.MultipleChoiceFilter(
|
||||
label=_("Representative version"),
|
||||
choices=[(True, _("Yes")), (False, _("Non"))],
|
||||
method="filter_by_representative",
|
||||
widget=forms.CheckboxSelectMultiple)
|
||||
|
||||
import_sources = django_filters.ModelChoiceFilter(
|
||||
label=_("Imported from"),
|
||||
method="filter_by_source",
|
||||
queryset=RecurrentImport.objects.all().order_by("name__unaccent")
|
||||
)
|
||||
|
||||
def filter_by_source(self, queryset, name, value):
|
||||
src = RecurrentImport.objects.get(pk=value.pk).source
|
||||
return queryset.filter(import_sources__contains=[src])
|
||||
|
||||
def filter_by_representative(self, queryset, name, value):
|
||||
if value is None or len(value) != 1:
|
||||
return queryset
|
||||
else:
|
||||
q = (Q(other_versions__isnull=True) |
|
||||
Q(other_versions__representative=F('pk')) |
|
||||
Q(other_versions__representative__isnull=True))
|
||||
if value[0] == True:
|
||||
return queryset.filter(q)
|
||||
else:
|
||||
return queryset.exclude(q)
|
||||
|
||||
|
||||
class Meta:
|
||||
model = Event
|
||||
fields = ["status"]
|
||||
|
||||
|
||||
class MessagesFilterAdmin(django_filters.FilterSet):
|
||||
closed = django_filters.MultipleChoiceFilter(
|
||||
label="Status",
|
||||
choices=((True, _("Closed")), (False, _("Open"))),
|
||||
widget=forms.CheckboxSelectMultiple,
|
||||
)
|
||||
spam = django_filters.MultipleChoiceFilter(
|
||||
label="Spam",
|
||||
choices=((True, _("Spam")), (False, _("Non spam"))),
|
||||
widget=forms.CheckboxSelectMultiple,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Message
|
||||
fields = ["closed", "spam"]
|
||||
|
||||
|
||||
class SimpleSearchEventFilter(django_filters.FilterSet):
|
||||
q = django_filters.CharFilter(method="custom_filter",
|
||||
label=_("Search"),
|
||||
widget=forms.TextInput(attrs={"type": "search"})
|
||||
)
|
||||
|
||||
status = django_filters.MultipleChoiceFilter(
|
||||
label="Filtrer par status",
|
||||
choices=Event.STATUS.choices,
|
||||
field_name="status",
|
||||
widget=forms.CheckboxSelectMultiple,
|
||||
)
|
||||
|
||||
def custom_filter(self, queryset, name, value):
|
||||
search_query = SearchQuery(value, config="french")
|
||||
qs = queryset.filter(
|
||||
Q(title__icontains=value)
|
||||
| Q(category__name__icontains=value)
|
||||
| Q(tags__icontains=[value])
|
||||
| Q(exact_location__name__icontains=value)
|
||||
| Q(description__icontains=value)
|
||||
)
|
||||
for f in ["title", "category__name", "exact_location__name", "description"]:
|
||||
params = {
|
||||
f
|
||||
+ "_hl": SearchHeadline(
|
||||
f,
|
||||
search_query,
|
||||
start_sel='<span class="highlight">',
|
||||
stop_sel="</span>",
|
||||
config="french",
|
||||
)
|
||||
}
|
||||
qs = qs.annotate(**params)
|
||||
return qs
|
||||
|
||||
class Meta:
|
||||
model = Event
|
||||
fields = ["q"]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
if not kwargs["request"].user.is_authenticated:
|
||||
self.form.fields.pop("status")
|
||||
|
||||
|
||||
class SearchEventFilter(django_filters.FilterSet):
|
||||
tags = django_filters.CharFilter(lookup_expr="icontains")
|
||||
title = django_filters.CharFilter(method="hl_filter_contains")
|
||||
location = django_filters.CharFilter(method="hl_filter_contains")
|
||||
description = django_filters.CharFilter(method="hl_filter_contains")
|
||||
start_day = django_filters.DateFromToRangeFilter(
|
||||
widget=django_filters.widgets.RangeWidget(attrs={"type": "date"})
|
||||
)
|
||||
status = django_filters.MultipleChoiceFilter(
|
||||
label="Filtrer par status",
|
||||
choices=Event.STATUS.choices,
|
||||
field_name="status",
|
||||
widget=forms.CheckboxSelectMultiple,
|
||||
)
|
||||
|
||||
o = django_filters.OrderingFilter(
|
||||
# tuple-mapping retains order
|
||||
fields=(
|
||||
("title", "title"),
|
||||
("description", "description"),
|
||||
("start_day", "start_day"),
|
||||
),
|
||||
)
|
||||
|
||||
def hl_filter_contains(self, queryset, name, value):
|
||||
# first check if it contains
|
||||
filter_contains = {name + "__contains": value}
|
||||
queryset = queryset.filter(**filter_contains)
|
||||
|
||||
# then hightlight the result
|
||||
search_query = SearchQuery(value, config="french")
|
||||
params = {
|
||||
name
|
||||
+ "_hl": SearchHeadline(
|
||||
name,
|
||||
search_query,
|
||||
start_sel='<span class="highlight">',
|
||||
stop_sel="</span>",
|
||||
config="french",
|
||||
)
|
||||
}
|
||||
return queryset.annotate(**params)
|
||||
|
||||
class Meta:
|
||||
model = Event
|
||||
fields = ["title", "location", "description", "category", "tags", "start_day"]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
if not kwargs["request"].user.is_authenticated:
|
||||
self.form.fields.pop("status")
|
||||
|
||||
|
||||
class DuplicatedEventsFilter(django_filters.FilterSet):
|
||||
fixed = django_filters.BooleanFilter(
|
||||
label="Résolu",
|
||||
field_name='representative', method="fixed_qs")
|
||||
|
||||
class Meta:
|
||||
model = DuplicatedEvents
|
||||
fields = []
|
||||
|
||||
|
||||
def fixed_qs(self, queryset, name, value):
|
||||
return DuplicatedEvents.not_fixed_qs(queryset, value)
|
||||
|
||||
|
||||
class RecurrentImportFilter(django_filters.FilterSet):
|
||||
|
||||
name = django_filters.ModelMultipleChoiceFilter(
|
||||
label="Filtrer par nom",
|
||||
field_name="name",
|
||||
queryset=RecurrentImport.objects.all().order_by("name__unaccent")
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = RecurrentImport
|
||||
fields = ["name"]
|
||||
|
@ -16,14 +16,15 @@ from django.forms import (
|
||||
)
|
||||
from django_better_admin_arrayfield.forms.widgets import DynamicArrayWidget
|
||||
|
||||
from .utils import PlaceGuesser
|
||||
from .models import (
|
||||
Event,
|
||||
RecurrentImport,
|
||||
CategorisationRule,
|
||||
ModerationAnswer,
|
||||
ModerationQuestion,
|
||||
Place,
|
||||
Category,
|
||||
Tag,
|
||||
Message
|
||||
)
|
||||
from django.conf import settings
|
||||
from django.core.files import File
|
||||
@ -32,24 +33,119 @@ from django.utils.translation import gettext_lazy as _
|
||||
from string import ascii_uppercase as auc
|
||||
from .templatetags.utils_extra import int_to_abc
|
||||
from django.utils.safestring import mark_safe
|
||||
from django.utils.timezone import localtime
|
||||
from django.utils.formats import localize
|
||||
from .templatetags.event_extra import event_field_verbose_name, field_to_html
|
||||
import os
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class GroupFormMixin:
|
||||
|
||||
template_name = 'agenda_culturel/forms/div_group.html'
|
||||
|
||||
class FieldGroup:
|
||||
|
||||
def __init__(self, id, label, display_label=False, maskable=False, default_masked=True):
|
||||
self.id = id
|
||||
self.label = label
|
||||
self.display_label = display_label
|
||||
self.maskable = maskable
|
||||
self.default_masked = default_masked
|
||||
|
||||
def toggle_field_name(self):
|
||||
return 'group_' + self.id
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.groups = []
|
||||
|
||||
def add_group(self, *args, **kwargs):
|
||||
self.groups.append(GroupFormMixin.FieldGroup(*args, **kwargs))
|
||||
if self.groups[-1].maskable:
|
||||
self.fields[self.groups[-1].toggle_field_name()] = BooleanField(required=False)
|
||||
self.fields[self.groups[-1].toggle_field_name()].toggle_group = True
|
||||
|
||||
def get_fields_in_group(self, g):
|
||||
return [f for f in self.visible_fields() if not hasattr(f.field, "toggle_group") and hasattr(f.field, "group_id") and f.field.group_id == g.id]
|
||||
|
||||
def get_no_group_fields(self):
|
||||
return [f for f in self.visible_fields() if not hasattr(f.field, "toggle_group") and (not hasattr(f.field, "group_id") or f.field.group_id == None)]
|
||||
|
||||
def fields_by_group(self):
|
||||
return [(g, self.get_fields_in_group(g)) for g in self.groups] + [(GroupFormMixin.FieldGroup("other", _("Other")), self.get_no_group_fields())]
|
||||
|
||||
def clean(self):
|
||||
result = super().clean()
|
||||
|
||||
if result:
|
||||
data = dict(self.data)
|
||||
# for each masked group, we remove data
|
||||
for g in self.groups:
|
||||
if g.maskable and not g.toggle_field_name() in data:
|
||||
fields = self.get_fields_in_group(g)
|
||||
for f in fields:
|
||||
self.cleaned_data[f.name] = None
|
||||
|
||||
return result
|
||||
|
||||
class TagForm(ModelForm):
|
||||
required_css_class = 'required'
|
||||
|
||||
class Meta:
|
||||
model = Tag
|
||||
fields = ["name", "description", "in_included_suggestions", "in_excluded_suggestions", "principal"]
|
||||
widgets = {
|
||||
"name": HiddenInput()
|
||||
}
|
||||
|
||||
class TagRenameForm(Form):
|
||||
required_css_class = 'required'
|
||||
|
||||
name = CharField(
|
||||
label=_('Name of new tag'),
|
||||
required=True
|
||||
)
|
||||
|
||||
force = BooleanField(
|
||||
label=_('Force renaming despite the existence of events already using the chosen tag.'),
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
force = kwargs.pop("force", False)
|
||||
name = kwargs.pop("name", None)
|
||||
super().__init__(*args, **kwargs)
|
||||
if not (force or (not len(args) == 0 and 'force' in args[0])):
|
||||
del self.fields["force"]
|
||||
if not name is None and self.fields["name"].initial is None:
|
||||
self.fields["name"].initial = name
|
||||
|
||||
|
||||
def is_force(self):
|
||||
return "force" in self.fields and self.cleaned_data["force"] == True
|
||||
|
||||
class URLSubmissionForm(Form):
|
||||
required_css_class = 'required'
|
||||
|
||||
url = URLField(max_length=512)
|
||||
category = ModelChoiceField(
|
||||
label=_("Category"),
|
||||
queryset=Category.objects.all().order_by("name"),
|
||||
initial=None,
|
||||
help_text=_('Optional. If you don''t specify a category, we''ll find it for you.'),
|
||||
required=False,
|
||||
)
|
||||
tags = MultipleChoiceField(
|
||||
label=_("Tags"),
|
||||
initial=None,
|
||||
choices=[],
|
||||
required=False
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields["tags"].choices = Tag.get_tag_groups(all=True)
|
||||
|
||||
|
||||
|
||||
|
||||
@ -62,22 +158,45 @@ class DynamicArrayWidgetTags(DynamicArrayWidget):
|
||||
|
||||
|
||||
class RecurrentImportForm(ModelForm):
|
||||
required_css_class = 'required'
|
||||
|
||||
defaultTags = MultipleChoiceField(
|
||||
label=_("Tags"),
|
||||
initial=None,
|
||||
choices=[],
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = RecurrentImport
|
||||
fields = "__all__"
|
||||
widgets = {
|
||||
"defaultTags": DynamicArrayWidgetTags(),
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields["defaultTags"].choices = Tag.get_tag_groups(all=True)
|
||||
|
||||
|
||||
class CategorisationRuleImportForm(ModelForm):
|
||||
required_css_class = 'required'
|
||||
|
||||
class Meta:
|
||||
model = CategorisationRule
|
||||
fields = "__all__"
|
||||
|
||||
|
||||
class EventForm(ModelForm):
|
||||
class EventForm(GroupFormMixin, ModelForm):
|
||||
required_css_class = 'required'
|
||||
|
||||
old_local_image = CharField(widget=HiddenInput(), required=False)
|
||||
simple_cloning = CharField(widget=HiddenInput(), required=False)
|
||||
|
||||
tags = MultipleChoiceField(
|
||||
label=_("Tags"),
|
||||
initial=None,
|
||||
choices=[],
|
||||
required=False
|
||||
)
|
||||
|
||||
|
||||
class Meta:
|
||||
model = Event
|
||||
@ -86,7 +205,11 @@ class EventForm(ModelForm):
|
||||
"modified_date",
|
||||
"moderated_date",
|
||||
"import_sources",
|
||||
"image"
|
||||
"image",
|
||||
"moderated_by_user",
|
||||
"modified_by_user",
|
||||
"created_by_user",
|
||||
"imported_by_user"
|
||||
]
|
||||
widgets = {
|
||||
"start_day": TextInput(
|
||||
@ -108,22 +231,73 @@ class EventForm(ModelForm):
|
||||
"other_versions": HiddenInput(),
|
||||
"uuids": MultipleHiddenInput(),
|
||||
"reference_urls": DynamicArrayWidgetURLs(),
|
||||
"tags": DynamicArrayWidgetTags(),
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
is_authenticated = kwargs.pop("is_authenticated", False)
|
||||
self.cloning = kwargs.pop("is_cloning", False)
|
||||
self.simple_cloning = kwargs.pop("is_simple_cloning", False)
|
||||
super().__init__(*args, **kwargs)
|
||||
if not is_authenticated:
|
||||
del self.fields["status"]
|
||||
del self.fields["organisers"]
|
||||
self.fields['category'].queryset = self.fields['category'].queryset.order_by('name')
|
||||
self.fields['category'].empty_label = None
|
||||
self.fields['category'].initial = Category.get_default_category()
|
||||
self.fields['tags'].choices = Tag.get_tag_groups(all=True)
|
||||
|
||||
# set groups
|
||||
self.add_group('main', _('Main fields'))
|
||||
self.fields['title'].group_id = 'main'
|
||||
|
||||
self.add_group('start', _('Start of event'))
|
||||
self.fields['start_day'].group_id = 'start'
|
||||
self.fields['start_time'].group_id = 'start'
|
||||
|
||||
self.add_group('end', _('End of event'))
|
||||
self.fields['end_day'].group_id = 'end'
|
||||
self.fields['end_time'].group_id = 'end'
|
||||
|
||||
self.add_group('recurrences',
|
||||
_('This is a recurring event'),
|
||||
maskable=True,
|
||||
default_masked=not (self.instance and
|
||||
self.instance.recurrences and
|
||||
self.instance.recurrences.rrules and
|
||||
len(self.instance.recurrences.rrules) > 0))
|
||||
|
||||
self.fields['recurrences'].group_id = 'recurrences'
|
||||
|
||||
self.add_group('details', _('Details'))
|
||||
self.fields['description'].group_id = 'details'
|
||||
if is_authenticated:
|
||||
self.fields['organisers'].group_id = 'details'
|
||||
|
||||
self.add_group('location', _('Location'))
|
||||
self.fields['location'].group_id = 'location'
|
||||
self.fields['exact_location'].group_id = 'location'
|
||||
|
||||
self.add_group('illustration', _('Illustration'))
|
||||
self.fields['local_image'].group_id = 'illustration'
|
||||
self.fields['image_alt'].group_id = 'illustration'
|
||||
|
||||
|
||||
if is_authenticated:
|
||||
self.add_group('meta-admin', _('Meta information'))
|
||||
self.fields['category'].group_id = 'meta-admin'
|
||||
self.fields['tags'].group_id = 'meta-admin'
|
||||
self.fields['status'].group_id = 'meta-admin'
|
||||
else:
|
||||
self.add_group('meta', _('Meta information'))
|
||||
self.fields['category'].group_id = 'meta'
|
||||
self.fields['tags'].group_id = 'meta'
|
||||
|
||||
def is_clone_from_url(self):
|
||||
return self.cloning
|
||||
|
||||
def is_simple_clone_from_url(self):
|
||||
return self.simple_cloning
|
||||
|
||||
def clean_end_day(self):
|
||||
start_day = self.cleaned_data.get("start_day")
|
||||
end_day = self.cleaned_data.get("end_day")
|
||||
@ -159,11 +333,69 @@ class EventForm(ModelForm):
|
||||
self.cleaned_data['old_local_image'] != "":
|
||||
basename = self.cleaned_data['old_local_image']
|
||||
old = settings.MEDIA_ROOT + "/" + basename
|
||||
self.cleaned_data['local_image'] = File(name=basename, file=open(old, "rb"))
|
||||
if os.path.isfile(old):
|
||||
self.cleaned_data['local_image'] = File(name=basename, file=open(old, "rb"))
|
||||
|
||||
|
||||
class MultipleChoiceFieldAcceptAll(MultipleChoiceField):
|
||||
def validate(self, value):
|
||||
pass
|
||||
|
||||
|
||||
class EventModerateForm(ModelForm):
|
||||
required_css_class = 'required'
|
||||
|
||||
tags = MultipleChoiceField(
|
||||
label=_("Tags"),
|
||||
help_text=_('Select tags from existing ones.'),
|
||||
required=False
|
||||
)
|
||||
|
||||
new_tags = MultipleChoiceFieldAcceptAll(
|
||||
label=_("New tags"),
|
||||
help_text=_('Create new labels (sparingly). Note: by starting your tag with the characters “TW:”, you''ll create a “trigger warning” tag, and the associated events will be announced as such.'),
|
||||
widget=DynamicArrayWidget(),
|
||||
required=False
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Event
|
||||
fields = [
|
||||
"status",
|
||||
"category",
|
||||
"organisers",
|
||||
"exact_location",
|
||||
"tags"
|
||||
]
|
||||
widgets = {
|
||||
"status": RadioSelect
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields['category'].queryset = self.fields['category'].queryset.order_by('name')
|
||||
self.fields['category'].empty_label = None
|
||||
self.fields['category'].initial = Category.get_default_category()
|
||||
self.fields['tags'].choices = Tag.get_tag_groups(all=True)
|
||||
|
||||
def clean_new_tags(self):
|
||||
return list(set(self.cleaned_data.get("new_tags")))
|
||||
|
||||
def clean(self):
|
||||
super().clean()
|
||||
|
||||
if self.cleaned_data['tags'] is None:
|
||||
self.cleaned_data['tags'] = []
|
||||
|
||||
if not self.cleaned_data.get('new_tags') is None:
|
||||
self.cleaned_data['tags'] += self.cleaned_data.get('new_tags')
|
||||
|
||||
self.cleaned_data['tags'] = list(set(self.cleaned_data['tags']))
|
||||
|
||||
|
||||
class BatchImportationForm(Form):
|
||||
required_css_class = 'required'
|
||||
|
||||
json = CharField(
|
||||
label="JSON",
|
||||
widget=Textarea(attrs={"rows": "10"}),
|
||||
@ -173,6 +405,8 @@ class BatchImportationForm(Form):
|
||||
|
||||
|
||||
class FixDuplicates(Form):
|
||||
required_css_class = 'required'
|
||||
|
||||
action = ChoiceField()
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@ -250,7 +484,6 @@ class FixDuplicates(Form):
|
||||
|
||||
def get_selected_event(self, edup):
|
||||
selected = self.get_selected_event_code()
|
||||
logger.warning("selected " + str(selected))
|
||||
for e in edup.get_duplicated():
|
||||
if e.pk == selected:
|
||||
return e
|
||||
@ -258,7 +491,9 @@ class FixDuplicates(Form):
|
||||
|
||||
|
||||
class SelectEventInList(Form):
|
||||
event = ChoiceField()
|
||||
required_css_class = 'required'
|
||||
|
||||
event = ChoiceField(label=_('Event'))
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
events = kwargs.pop("events", None)
|
||||
@ -270,7 +505,9 @@ class SelectEventInList(Form):
|
||||
|
||||
|
||||
class MergeDuplicates(Form):
|
||||
checkboxes_fields = ["reference_urls", "description"]
|
||||
required_css_class = 'required'
|
||||
|
||||
checkboxes_fields = ["reference_urls", "description", "tags"]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.duplicates = kwargs.pop("duplicates", None)
|
||||
@ -311,17 +548,17 @@ class MergeDuplicates(Form):
|
||||
'<li><a href="' + e.get_absolute_url() + '">' + e.title + "</a></li>"
|
||||
)
|
||||
result += (
|
||||
"<li>Création : " + localize(localtime(e.created_date)) + "</li>"
|
||||
"<li>Création : " + localize(e.created_date) + "</li>"
|
||||
)
|
||||
result += (
|
||||
"<li>Dernière modification : "
|
||||
+ localize(localtime(e.modified_date))
|
||||
+ localize(e.modified_date)
|
||||
+ "</li>"
|
||||
)
|
||||
if e.imported_date:
|
||||
result += (
|
||||
"<li>Dernière importation : "
|
||||
+ localize(localtime(e.imported_date))
|
||||
+ localize(e.imported_date)
|
||||
+ "</li>"
|
||||
)
|
||||
result += "</ul>"
|
||||
@ -372,7 +609,7 @@ class MergeDuplicates(Form):
|
||||
result += '<input id="' + id + '" name="' + key + '"'
|
||||
if key in MergeDuplicates.checkboxes_fields:
|
||||
result += ' type="checkbox"'
|
||||
if value in checked:
|
||||
if checked and value in checked:
|
||||
result += " checked"
|
||||
else:
|
||||
result += ' type="radio"'
|
||||
@ -404,7 +641,7 @@ class MergeDuplicates(Form):
|
||||
result = []
|
||||
for s in selected:
|
||||
for e in self.duplicates.get_duplicated():
|
||||
if e.pk == selected:
|
||||
if e.pk == s:
|
||||
result.append(e)
|
||||
break
|
||||
return result
|
||||
@ -417,47 +654,9 @@ class MergeDuplicates(Form):
|
||||
return None
|
||||
|
||||
|
||||
class ModerationQuestionForm(ModelForm):
|
||||
class Meta:
|
||||
model = ModerationQuestion
|
||||
fields = "__all__"
|
||||
|
||||
|
||||
class ModerationAnswerForm(ModelForm):
|
||||
class Meta:
|
||||
model = ModerationAnswer
|
||||
exclude = ["question"]
|
||||
widgets = {
|
||||
"adds_tags": DynamicArrayWidgetTags(),
|
||||
"removes_tags": DynamicArrayWidgetTags(),
|
||||
}
|
||||
|
||||
|
||||
class ModerateForm(ModelForm):
|
||||
class Meta:
|
||||
model = Event
|
||||
fields = []
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
mqs = ModerationQuestion.objects.all()
|
||||
mas = ModerationAnswer.objects.all()
|
||||
|
||||
for q in mqs:
|
||||
self.fields[q.complete_id()] = ChoiceField(
|
||||
widget=RadioSelect,
|
||||
label=q.question,
|
||||
choices=[(a.pk, a.html_description()) for a in mas if a.question == q],
|
||||
required=True,
|
||||
)
|
||||
for a in mas:
|
||||
if a.question == q and a.valid_event(self.instance):
|
||||
self.fields[q.complete_id()].initial = a.pk
|
||||
break
|
||||
|
||||
|
||||
class CategorisationForm(Form):
|
||||
required_css_class = 'required'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
if "events" in kwargs:
|
||||
events = kwargs.pop("events", None)
|
||||
@ -488,6 +687,8 @@ class CategorisationForm(Form):
|
||||
|
||||
|
||||
class EventAddPlaceForm(Form):
|
||||
required_css_class = 'required'
|
||||
|
||||
place = ModelChoiceField(
|
||||
label=_("Place"),
|
||||
queryset=Place.objects.all().order_by("name"),
|
||||
@ -524,7 +725,9 @@ class EventAddPlaceForm(Form):
|
||||
return self.instance
|
||||
|
||||
|
||||
class PlaceForm(ModelForm):
|
||||
class PlaceForm(GroupFormMixin, ModelForm):
|
||||
required_css_class = 'required'
|
||||
|
||||
apply_to_all = BooleanField(
|
||||
initial=True,
|
||||
label=_(
|
||||
@ -538,13 +741,70 @@ class PlaceForm(ModelForm):
|
||||
fields = "__all__"
|
||||
widgets = {"location": TextInput()}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.add_group('header', _('Header'))
|
||||
self.fields['name'].group_id = 'header'
|
||||
|
||||
|
||||
self.add_group('address', _('Address'))
|
||||
self.fields['address'].group_id = 'address'
|
||||
self.fields['postcode'].group_id = 'address'
|
||||
self.fields['city'].group_id = 'address'
|
||||
self.fields['location'].group_id = 'address'
|
||||
|
||||
self.add_group('meta', _('Meta'))
|
||||
self.fields['aliases'].group_id = 'meta'
|
||||
|
||||
self.add_group('information', _('Information'))
|
||||
self.fields['description'].group_id = 'information'
|
||||
|
||||
def as_grid(self):
|
||||
return mark_safe(
|
||||
'<div class="grid"><div>'
|
||||
result = ('<div class="grid"><div>'
|
||||
+ super().as_p()
|
||||
+ '</div><div><div class="map-widget">'
|
||||
+ '<div id="map_location" style="width: 100%; aspect-ratio: 16/9"></div><p>Cliquez pour ajuster la position GPS</p></div></div></div>'
|
||||
)
|
||||
+ '''</div><div><div class="map-widget">
|
||||
<div id="map_location" style="width: 100%; aspect-ratio: 16/9"></div>
|
||||
<p>Cliquez pour ajuster la position GPS</p></div>
|
||||
<input type="checkbox" role="switch" id="lock_position">Verrouiller la position</lock>
|
||||
<script>
|
||||
document.getElementById("lock_position").onclick = function() {
|
||||
const field = document.getElementById("id_location");
|
||||
if (this.checked)
|
||||
field.setAttribute("readonly", true);
|
||||
else
|
||||
field.removeAttribute("readonly");
|
||||
}
|
||||
</script>
|
||||
</div></div>''')
|
||||
|
||||
return mark_safe(result)
|
||||
|
||||
def apply(self):
|
||||
return self.cleaned_data.get("apply_to_all")
|
||||
|
||||
class MessageForm(ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = Message
|
||||
fields = ["subject", "name", "email", "message", "related_event"]
|
||||
widgets = {"related_event": HiddenInput(), "user": HiddenInput() }
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.event = kwargs.pop("event", False)
|
||||
self.internal = kwargs.pop("internal", False)
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields['related_event'].required = False
|
||||
if self.internal:
|
||||
self.fields.pop("name")
|
||||
self.fields.pop("email")
|
||||
|
||||
class MessageEventForm(ModelForm):
|
||||
|
||||
class Meta:
|
||||
model = Message
|
||||
fields = ["message"]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields["message"].label = _("Add a comment")
|
@ -70,17 +70,17 @@ class CExtractor(TwoStepsExtractorNoPause):
|
||||
tags = []
|
||||
if first_cat in ["grand spectacle"]:
|
||||
category = "Spectacles"
|
||||
tags.append("danse")
|
||||
tags.append("💃 danse")
|
||||
elif first_cat in ["theatre", "humour / one man show"]:
|
||||
category = "Spectacles"
|
||||
tags.append("théâtre")
|
||||
tags.append("🎭 théâtre")
|
||||
elif first_cat in ["chanson francaise", "musique du monde", "pop / rock", "rap", "rnb", "raggae", "variete"]:
|
||||
category = "Fêtes & Concerts"
|
||||
tags.append("concert")
|
||||
tags.append("🎵 concert")
|
||||
elif first_cat in ["comedie musicale", "humour / one man show", "spectacle equestre"]:
|
||||
category = "Spectacles"
|
||||
elif first_cat in ["spectacle pour enfant"]:
|
||||
tags = ["jeune public"]
|
||||
tags = ["🎈 jeune public"]
|
||||
category = None
|
||||
else:
|
||||
category = None
|
||||
|
@ -11,7 +11,7 @@ class CExtractor(TwoStepsExtractor):
|
||||
if not category:
|
||||
return None
|
||||
mapping = {"Théâtre": "Spectacles", "Concert": "Fêtes & Concerts", "Projection": "Cinéma"}
|
||||
mapping_tag = {"Théâtre": "théâtre", "Concert": "concert", "Projection": None}
|
||||
mapping_tag = {"Théâtre": "🎭 théâtre", "Concert": "🎵 concert", "Projection": None}
|
||||
if category in mapping:
|
||||
return mapping[category], mapping_tag[category]
|
||||
else:
|
||||
|
@ -3,6 +3,12 @@ from ..extractor_facebook import FacebookEvent
|
||||
import json5
|
||||
from bs4 import BeautifulSoup
|
||||
import json
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# A class dedicated to get events from a facebook events page
|
||||
@ -13,10 +19,27 @@ class CExtractor(TwoStepsExtractor):
|
||||
def build_event_url_list(self, content):
|
||||
soup = BeautifulSoup(content, "html.parser")
|
||||
|
||||
debug = False
|
||||
|
||||
found = False
|
||||
links = soup.find_all("a")
|
||||
for link in links:
|
||||
if link.get("href").startswith('https://www.facebook.com/events/'):
|
||||
self.add_event_url(link.get('href').split('?')[0])
|
||||
found = True
|
||||
|
||||
if not found and debug:
|
||||
directory = "errors/"
|
||||
if not os.path.exists(directory):
|
||||
os.makedirs(directory)
|
||||
now = datetime.now()
|
||||
filename = directory + now.strftime("%Y%m%d_%H%M%S") + ".html"
|
||||
logger.warning("cannot find any event link in events page. Save content page in " + filename)
|
||||
with open(filename, "w") as text_file:
|
||||
text_file.write("<!-- " + self.url + " -->\n\n")
|
||||
text_file.write(content)
|
||||
|
||||
|
||||
|
||||
|
||||
def add_event_from_content(
|
||||
@ -42,4 +65,7 @@ class CExtractor(TwoStepsExtractor):
|
||||
event["published"] = published
|
||||
|
||||
self.add_event(default_values, **event)
|
||||
else:
|
||||
logger.warning("cannot find any event in page")
|
||||
|
||||
|
||||
|
@ -9,6 +9,12 @@ class CExtractor(TwoStepsExtractor):
|
||||
nom_lieu = "La Comédie de Clermont"
|
||||
url_referer = "https://lacomediedeclermont.com/saison24-25/"
|
||||
|
||||
def is_to_import_from_url(self, url):
|
||||
if any(keyword in url for keyword in ["podcast", "on-debriefe", "popcorn", "rencontreautour","rencontre-autour"]):
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def category_comedie2agenda(self, category):
|
||||
mapping = {
|
||||
"Théâtre": "Spectacles",
|
||||
@ -18,8 +24,8 @@ class CExtractor(TwoStepsExtractor):
|
||||
"PopCorn Live": "Sans catégorie",
|
||||
}
|
||||
mapping_tag = {
|
||||
"Théâtre": "théâtre",
|
||||
"Danse": "danse",
|
||||
"Théâtre": "🎭 théâtre",
|
||||
"Danse": "💃 danse",
|
||||
"Rencontre": None,
|
||||
"Sortie de résidence": "sortie de résidence",
|
||||
"PopCorn Live": None,
|
||||
@ -50,33 +56,35 @@ class CExtractor(TwoStepsExtractor):
|
||||
e_url = (
|
||||
e.select("a")[0]["href"] + "#" + d
|
||||
) # a "fake" url specific for each day of this show
|
||||
self.add_event_url(e_url)
|
||||
self.add_event_start_day(e_url, d)
|
||||
t = (
|
||||
str(e.select("div#datecal")[0])
|
||||
.split(" ")[-1]
|
||||
.split("<")[0]
|
||||
)
|
||||
self.add_event_start_time(e_url, t)
|
||||
title = e.select("a")[0].contents[0]
|
||||
self.add_event_title(e_url, title)
|
||||
category = e.select("div#lieuevtcal span")
|
||||
if len(category) > 0:
|
||||
category, tag = self.category_comedie2agenda(
|
||||
category[-1].contents[0]
|
||||
|
||||
if self.is_to_import_from_url(e_url):
|
||||
self.add_event_url(e_url)
|
||||
self.add_event_start_day(e_url, d)
|
||||
t = (
|
||||
str(e.select("div#datecal")[0])
|
||||
.split(" ")[-1]
|
||||
.split("<")[0]
|
||||
)
|
||||
if category:
|
||||
self.add_event_category(e_url, category)
|
||||
if tag:
|
||||
self.add_event_tag(e_url, tag)
|
||||
location = (
|
||||
e.select("div#lieuevtcal")[0]
|
||||
.contents[-1]
|
||||
.split("•")[-1]
|
||||
)
|
||||
if location.replace(" ", "") == "":
|
||||
location = self.nom_lieu
|
||||
self.add_event_location(e_url, location)
|
||||
self.add_event_start_time(e_url, t)
|
||||
title = e.select("a")[0].contents[0]
|
||||
self.add_event_title(e_url, title)
|
||||
category = e.select("div#lieuevtcal span")
|
||||
if len(category) > 0:
|
||||
category, tag = self.category_comedie2agenda(
|
||||
category[-1].contents[0]
|
||||
)
|
||||
if category:
|
||||
self.add_event_category(e_url, category)
|
||||
if tag:
|
||||
self.add_event_tag(e_url, tag)
|
||||
location = (
|
||||
e.select("div#lieuevtcal")[0]
|
||||
.contents[-1]
|
||||
.split("•")[-1]
|
||||
)
|
||||
if location.replace(" ", "") == "":
|
||||
location = self.nom_lieu
|
||||
self.add_event_location(e_url, location)
|
||||
|
||||
def add_event_from_content(
|
||||
self,
|
||||
@ -98,6 +106,15 @@ class CExtractor(TwoStepsExtractor):
|
||||
description = soup.select("#descspec")
|
||||
if description and len(description) > 0:
|
||||
description = description[0].get_text().replace("Lire plus...", "")
|
||||
# on ajoute éventuellement les informations complémentaires
|
||||
|
||||
d_suite = ""
|
||||
for d in ["typedesc", "dureedesc", "lieuspec"]:
|
||||
comp_desc = soup.select("#" + d)
|
||||
if comp_desc and len(comp_desc) > 0:
|
||||
d_suite += "\n\n" + comp_desc[0].get_text()
|
||||
if d_suite != "":
|
||||
description += "\n\n> Informations complémentaires:" + d_suite
|
||||
else:
|
||||
description = None
|
||||
|
||||
|
@ -22,7 +22,7 @@ class CExtractor(TwoStepsExtractor):
|
||||
for e in data["events"]:
|
||||
self.add_event_url(e["url"])
|
||||
if e["tag"] == "Gratuit":
|
||||
self.add_event_tag(e["url"], "gratuit")
|
||||
self.add_event_tag(e["url"], "💶 gratuit")
|
||||
|
||||
else:
|
||||
raise Exception("Cannot extract events from javascript")
|
||||
@ -53,7 +53,7 @@ class CExtractor(TwoStepsExtractor):
|
||||
if description is None:
|
||||
description = ""
|
||||
|
||||
tags = ["concert"]
|
||||
tags = ["🎵 concert"]
|
||||
|
||||
link_calendar = soup.select('a[href^="https://calendar.google.com/calendar/"]')
|
||||
if len(link_calendar) == 0:
|
||||
|
@ -58,7 +58,7 @@ class CExtractor(TwoStepsExtractor):
|
||||
end_day = Extractor.guess_end_day(start_day, start_time, end_time)
|
||||
|
||||
url_human = event_url
|
||||
tags = ["concert"]
|
||||
tags = ["🎵 concert"]
|
||||
|
||||
image = soup.select("wow-image img[fetchpriority=high]")
|
||||
if image:
|
||||
|
@ -10,7 +10,7 @@ class CExtractor(TwoStepsExtractor):
|
||||
if not category:
|
||||
return None
|
||||
mapping = {"Concerts": "Fêtes & Concerts"}
|
||||
mapping_tag = {"Concerts": "concert"}
|
||||
mapping_tag = {"Concerts": "🎵 concert"}
|
||||
if category in mapping:
|
||||
return mapping[category], mapping_tag[category]
|
||||
else:
|
||||
|
91
src/agenda_culturel/import_tasks/custom_extractors/lerio.py
Normal file
91
src/agenda_culturel/import_tasks/custom_extractors/lerio.py
Normal file
@ -0,0 +1,91 @@
|
||||
from ..generic_extractors import *
|
||||
from bs4 import BeautifulSoup
|
||||
from datetime import datetime
|
||||
|
||||
# A class dedicated to get events from Cinéma Le Rio (Clermont-Ferrand)
|
||||
# URL: https://www.cinemalerio.com/evenements/
|
||||
class CExtractor(TwoStepsExtractorNoPause):
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.possible_dates = {}
|
||||
self.theater = None
|
||||
|
||||
def build_event_url_list(self, content, infuture_days=180):
|
||||
|
||||
soup = BeautifulSoup(content, "html.parser")
|
||||
|
||||
links = soup.select("td.seance_link a")
|
||||
if links:
|
||||
for l in links:
|
||||
print(l["href"])
|
||||
self.add_event_url(l["href"])
|
||||
|
||||
def to_text_select_one(soup, filter):
|
||||
e = soup.select_one(filter)
|
||||
if e is None:
|
||||
return None
|
||||
else:
|
||||
return e.text
|
||||
|
||||
def add_event_from_content(
|
||||
self,
|
||||
event_content,
|
||||
event_url,
|
||||
url_human=None,
|
||||
default_values=None,
|
||||
published=False,
|
||||
):
|
||||
|
||||
soup = BeautifulSoup(event_content, "html.parser")
|
||||
|
||||
title = soup.select_one("h1").text
|
||||
|
||||
alerte_date = CExtractor.to_text_select_one(soup, ".alerte_date")
|
||||
if alerte_date is None:
|
||||
return
|
||||
dh = alerte_date.split("à")
|
||||
# if date is not found, we skip
|
||||
if len(dh) != 2:
|
||||
return
|
||||
|
||||
date = Extractor.parse_french_date(dh[0], default_year=datetime.now().year)
|
||||
time = Extractor.parse_french_time(dh[1])
|
||||
|
||||
synopsis = CExtractor.to_text_select_one(soup, ".synopsis_bloc")
|
||||
special_titre = CExtractor.to_text_select_one(soup, ".alerte_titre")
|
||||
special = CExtractor.to_text_select_one(soup, ".alerte_text")
|
||||
|
||||
# it's not a specific event: we skip it
|
||||
special_lines = None if special is None else special.split('\n')
|
||||
if special is None or len(special_lines) == 0 or \
|
||||
(len(special_lines) == 1 and special_lines[0].strip().startswith('En partenariat')):
|
||||
return
|
||||
|
||||
description = "\n\n".join([x for x in [synopsis, special_titre, special] if not x is None])
|
||||
|
||||
image = soup.select_one(".col1 img")
|
||||
image_alt = None
|
||||
if not image is None:
|
||||
image_alt = image["alt"]
|
||||
image = image["src"]
|
||||
|
||||
self.add_event_with_props(
|
||||
default_values,
|
||||
event_url,
|
||||
title,
|
||||
None,
|
||||
date,
|
||||
None,
|
||||
description,
|
||||
[],
|
||||
recurrences=None,
|
||||
uuids=[event_url],
|
||||
url_human=event_url,
|
||||
start_time=time,
|
||||
end_day=None,
|
||||
end_time=None,
|
||||
published=published,
|
||||
image=image,
|
||||
image_alt=image_alt
|
||||
)
|
@ -66,7 +66,7 @@ class SimpleDownloader(Downloader):
|
||||
|
||||
|
||||
class ChromiumHeadlessDownloader(Downloader):
|
||||
def __init__(self, pause=True):
|
||||
def __init__(self, pause=True, noimage=True):
|
||||
super().__init__()
|
||||
self.pause = pause
|
||||
self.options = Options()
|
||||
@ -78,17 +78,31 @@ class ChromiumHeadlessDownloader(Downloader):
|
||||
self.options.add_argument("--disable-dev-shm-usage")
|
||||
self.options.add_argument("--disable-browser-side-navigation")
|
||||
self.options.add_argument("--disable-gpu")
|
||||
self.options.add_experimental_option(
|
||||
"prefs", {
|
||||
# block image loading
|
||||
"profile.managed_default_content_settings.images": 2,
|
||||
}
|
||||
)
|
||||
if noimage:
|
||||
self.options.add_experimental_option(
|
||||
"prefs", {
|
||||
# block image loading
|
||||
"profile.managed_default_content_settings.images": 2,
|
||||
}
|
||||
)
|
||||
|
||||
self.service = Service("/usr/bin/chromedriver")
|
||||
self.driver = webdriver.Chrome(service=self.service, options=self.options)
|
||||
|
||||
|
||||
def screenshot(self, url, path_image):
|
||||
print("Screenshot {}".format(url))
|
||||
try:
|
||||
self.driver.get(url)
|
||||
if self.pause:
|
||||
time.sleep(2)
|
||||
self.driver.save_screenshot(path_image)
|
||||
except:
|
||||
print(f">> Exception: {URL}")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def download(self, url, referer=None, post=None):
|
||||
if post:
|
||||
raise Exception("POST method with Chromium headless not yet implemented")
|
||||
|
@ -2,7 +2,7 @@ from abc import ABC, abstractmethod
|
||||
from datetime import datetime, time, date, timedelta
|
||||
import re
|
||||
import unicodedata
|
||||
|
||||
from django.utils import timezone
|
||||
|
||||
|
||||
|
||||
@ -49,7 +49,7 @@ class Extractor(ABC):
|
||||
return i + 1
|
||||
return None
|
||||
|
||||
def parse_french_date(text):
|
||||
def parse_french_date(text, default_year=None):
|
||||
# format NomJour Numero Mois Année
|
||||
m = re.search(
|
||||
"[a-zA-ZéÉûÛ:.]+[ ]*([0-9]+)[er]*[ ]*([a-zA-ZéÉûÛ:.]+)[ ]*([0-9]+)", text
|
||||
@ -73,8 +73,15 @@ class Extractor(ABC):
|
||||
month = int(m.group(2))
|
||||
year = m.group(3)
|
||||
else:
|
||||
# TODO: consolider les cas non satisfaits
|
||||
return None
|
||||
# format Numero Mois Annee
|
||||
m = re.search("([0-9]+)[er]*[ ]*([a-zA-ZéÉûÛ:.]+)", text)
|
||||
if m:
|
||||
day = m.group(1)
|
||||
month = Extractor.guess_month(m.group(2))
|
||||
year = default_year
|
||||
else:
|
||||
# TODO: consolider les cas non satisfaits
|
||||
return None
|
||||
|
||||
if month is None:
|
||||
return None
|
||||
@ -187,6 +194,7 @@ class Extractor(ABC):
|
||||
"start_day": start_day,
|
||||
"uuids": uuids,
|
||||
"location": location if location else self.default_value_if_exists(default_values, "location"),
|
||||
"organisers": self.default_value_if_exists(default_values, "organisers"),
|
||||
"description": description,
|
||||
"tags": tags + tags_default,
|
||||
"published": published,
|
||||
@ -239,6 +247,28 @@ class Extractor(ABC):
|
||||
from .extractor_ggcal_link import GoogleCalendarLinkEventExtractor
|
||||
|
||||
if single_event:
|
||||
return [FacebookEventExtractor(), GoogleCalendarLinkEventExtractor()]
|
||||
return [FacebookEventExtractor(), GoogleCalendarLinkEventExtractor(), EventNotFoundExtractor()]
|
||||
else:
|
||||
return [ICALExtractor(), FacebookEventExtractor(), GoogleCalendarLinkEventExtractor()]
|
||||
return [ICALExtractor(), FacebookEventExtractor(), GoogleCalendarLinkEventExtractor(), EventNotFoundExtractor()]
|
||||
|
||||
|
||||
# A class that only produce a not found event
|
||||
class EventNotFoundExtractor(Extractor):
|
||||
|
||||
def extract(
|
||||
self, content, url, url_human=None, default_values=None, published=False
|
||||
):
|
||||
self.set_header(url)
|
||||
self.clear_events()
|
||||
|
||||
self.add_event(default_values, "événement sans titre depuis " + url,
|
||||
None, timezone.now().date(), None,
|
||||
"l'import a échoué, la saisie doit se faire manuellement à partir de l'url source " + url,
|
||||
[], [url], published=False, url_human=url)
|
||||
|
||||
return self.get_structure()
|
||||
|
||||
|
||||
def clean_url(url):
|
||||
return url
|
||||
|
||||
|
@ -239,7 +239,7 @@ class FacebookEventExtractor(Extractor):
|
||||
result = "https://www.facebook.com" + u.path
|
||||
|
||||
# remove name in the url
|
||||
match = re.match(r"(.*/events)/s/([a-zA-Z-][a-zA-Z-0-9]+)/([0-9/]*)", result)
|
||||
match = re.match(r"(.*/events)/s/([a-zA-Z-][a-zA-Z-0-9-]+)/([0-9/]*)", result)
|
||||
if match:
|
||||
result = match[1] + "/" + match[3]
|
||||
|
||||
|
@ -264,9 +264,13 @@ class TwoStepsExtractorNoPause(TwoStepsExtractor):
|
||||
only_future=True,
|
||||
ignore_404=True
|
||||
):
|
||||
pause = self.downloader.pause
|
||||
if hasattr(self.downloader, "pause"):
|
||||
pause = self.downloader.pause
|
||||
else:
|
||||
pause = False
|
||||
self.downloader.pause = False
|
||||
result = super().extract(content, url, url_human, default_values, published, only_future, ignore_404)
|
||||
self.downloader.pause = pause
|
||||
|
||||
return result
|
||||
|
||||
|
@ -1,6 +1,11 @@
|
||||
from .downloader import *
|
||||
from .extractor import *
|
||||
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
||||
class URL2Events:
|
||||
def __init__(
|
||||
@ -29,8 +34,9 @@ class URL2Events:
|
||||
else:
|
||||
# if the extractor is not defined, use a list of default extractors
|
||||
for e in Extractor.get_default_extractors(self.single_event):
|
||||
logger.warning('Extractor::' + type(e).__name__)
|
||||
e.set_downloader(self.downloader)
|
||||
events = e.extract(content, url, url_human, default_values, published)
|
||||
if events is not None:
|
||||
if events is not None and len(events) > 0:
|
||||
return events
|
||||
return None
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,11 +1,11 @@
|
||||
# Generated by Django 4.2.9 on 2024-10-10 20:35
|
||||
|
||||
from django.db import migrations
|
||||
from agenda_culturel.models import Place
|
||||
from django.contrib.gis.geos import Point
|
||||
|
||||
def change_coord_format(apps, schema_editor):
|
||||
places = Place.objects.all()
|
||||
Place = apps.get_model("agenda_culturel", "Place")
|
||||
places = Place.objects.values("location", "location_pt").all()
|
||||
|
||||
for p in places:
|
||||
l = p.location.split(',')
|
||||
@ -13,14 +13,15 @@ def change_coord_format(apps, schema_editor):
|
||||
p.location_pt = Point(float(l[1]), float(l[0]))
|
||||
else:
|
||||
p.location_pt = Point(3.08333, 45.783329)
|
||||
p.save()
|
||||
p.save(update_fields=["location_pt"])
|
||||
|
||||
def reverse_coord_format(apps, schema_editor):
|
||||
places = Place.objects.all()
|
||||
Place = apps.get_model("agenda_culturel", "Place")
|
||||
places = Place.objects.values("location", "location_pt").all()
|
||||
|
||||
for p in places:
|
||||
p.location = ','.join([p.location_pt[1], p.location_pt[0]])
|
||||
p.save()
|
||||
p.save(update_fields=["location"])
|
||||
|
||||
|
||||
|
||||
|
@ -184,15 +184,6 @@ def update_database_new(apps, schema_editor):
|
||||
def update_database_old(apps, schema_editor):
|
||||
update_database(apps, new_cats)
|
||||
|
||||
def add_tags(apps, schema_editor):
|
||||
Tag = apps.get_model("agenda_culturel", "Tag")
|
||||
|
||||
new_tags = ["cinéma", "théâtre", "concert", "conférence", "exposition"]
|
||||
|
||||
new_tags = [Tag(name=t, description="", principal=True) for t in new_tags if Tag.objects.filter(name=t).count() == 0]
|
||||
|
||||
Tag.objects.bulk_create(new_tags)
|
||||
|
||||
def do_nothing(apps, schema_editor):
|
||||
pass
|
||||
|
||||
@ -206,7 +197,6 @@ class Migration(migrations.Migration):
|
||||
migrations.RunPython(create_new_categories, reverse_code=delete_new_categories),
|
||||
migrations.RunPython(update_preserved_categories_new, reverse_code=update_preserved_categories_old),
|
||||
migrations.RunPython(update_database_new, reverse_code=update_database_old),
|
||||
migrations.RunPython(delete_old_categories, reverse_code=create_old_categories),
|
||||
migrations.RunPython(add_tags, reverse_code=do_nothing)
|
||||
migrations.RunPython(delete_old_categories, reverse_code=create_old_categories)
|
||||
]
|
||||
|
||||
|
@ -0,0 +1,19 @@
|
||||
# Generated by Django 4.2.9 on 2024-11-13 09:56
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('agenda_culturel', '0108_remove_duplicated_categories'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.DeleteModel(
|
||||
name='ModerationAnswer',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='ModerationQuestion',
|
||||
),
|
||||
]
|
@ -0,0 +1,23 @@
|
||||
# Generated by Django 4.2.9 on 2024-11-13 17:27
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('agenda_culturel', '0109_delete_moderationanswer_delete_moderationquestion'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='tag',
|
||||
name='in_excluded_suggestions',
|
||||
field=models.BooleanField(default=False, help_text='This tag will be part of the excluded suggestions.', verbose_name='In excluded suggestions'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='tag',
|
||||
name='in_included_suggestions',
|
||||
field=models.BooleanField(default=False, help_text='This tag will be part of the included suggestions.', verbose_name='In included suggestions'),
|
||||
),
|
||||
]
|
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.9 on 2024-11-17 12:48
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('agenda_culturel', '0110_tag_in_excluded_suggestions_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='referencelocation',
|
||||
name='main',
|
||||
field=models.IntegerField(default=0, help_text='This location is one of the main locations (shown first higher values).', verbose_name='Main'),
|
||||
),
|
||||
]
|
19
src/agenda_culturel/migrations/0112_place_description.py
Normal file
19
src/agenda_culturel/migrations/0112_place_description.py
Normal file
@ -0,0 +1,19 @@
|
||||
# Generated by Django 4.2.9 on 2024-11-20 15:42
|
||||
|
||||
from django.db import migrations
|
||||
import django_ckeditor_5.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('agenda_culturel', '0111_alter_referencelocation_main'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='place',
|
||||
name='description',
|
||||
field=django_ckeditor_5.fields.CKEditor5Field(blank=True, help_text='Description of the place, including accessibility.', null=True, verbose_name='Description'),
|
||||
),
|
||||
]
|
17
src/agenda_culturel/migrations/0113_remove_tag_category.py
Normal file
17
src/agenda_culturel/migrations/0113_remove_tag_category.py
Normal file
@ -0,0 +1,17 @@
|
||||
# Generated by Django 4.2.9 on 2024-11-20 21:40
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('agenda_culturel', '0112_place_description'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='tag',
|
||||
name='category',
|
||||
),
|
||||
]
|
@ -0,0 +1,35 @@
|
||||
# Generated by Django 4.2.9 on 2024-11-22 10:12
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django_ckeditor_5.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('agenda_culturel', '0113_remove_tag_category'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Organisation',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(help_text='Organisation name', max_length=512, unique=True, verbose_name='Name')),
|
||||
('website', models.URLField(blank=True, help_text='Website of the organisation', max_length=1024, null=True, verbose_name='Website')),
|
||||
('description', django_ckeditor_5.fields.CKEditor5Field(blank=True, help_text='Description of the organisation.', null=True, verbose_name='Description')),
|
||||
('principal_place', models.ForeignKey(blank=True, help_text='Place mainly associated with this organizer. Mainly used if there is a similarity in the name, to avoid redundant displays.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='agenda_culturel.place', verbose_name='Principal place')),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='event',
|
||||
name='organisers',
|
||||
field=models.ManyToManyField(blank=True, help_text='list of event organisers. Organizers will only be displayed if one of them does not normally use the venue.', related_name='organised_events', to='agenda_culturel.organisation', verbose_name='Location (free form)'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='recurrentimport',
|
||||
name='defaultOrganiser',
|
||||
field=models.ForeignKey(blank=True, default=None, help_text='Organiser of each imported event', null=True, on_delete=django.db.models.deletion.SET_DEFAULT, to='agenda_culturel.organisation', verbose_name='Organiser'),
|
||||
),
|
||||
]
|
@ -0,0 +1,22 @@
|
||||
# Generated by Django 4.2.9 on 2024-11-22 10:30
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('agenda_culturel', '0114_organisation_event_organisers_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='organisation',
|
||||
options={'verbose_name': 'Organisation', 'verbose_name_plural': 'Organisations'},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='organisers',
|
||||
field=models.ManyToManyField(blank=True, help_text='list of event organisers. Organizers will only be displayed if one of them does not normally use the venue.', related_name='organised_events', to='agenda_culturel.organisation', verbose_name='Organisers'),
|
||||
),
|
||||
]
|
@ -0,0 +1,17 @@
|
||||
# Generated by Django 4.2.9 on 2024-11-23 09:58
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('agenda_culturel', '0115_alter_organisation_options_alter_event_organisers'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddIndex(
|
||||
model_name='event',
|
||||
index=models.Index(fields=['start_day', 'start_time'], name='agenda_cult_start_d_68ab5f_idx'),
|
||||
),
|
||||
]
|
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.9 on 2024-11-23 10:11
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.functions.text
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('agenda_culturel', '0116_event_agenda_cult_start_d_68ab5f_idx'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddIndex(
|
||||
model_name='event',
|
||||
index=models.Index(models.F('start_time'), django.db.models.functions.text.Lower('title'), name='start_time title'),
|
||||
),
|
||||
]
|
@ -0,0 +1,33 @@
|
||||
# Generated by Django 4.2.9 on 2024-11-23 10:16
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('agenda_culturel', '0117_event_start_time_title'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='tag',
|
||||
options={'verbose_name': 'Étiquette', 'verbose_name_plural': 'Étiquettes'},
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='category',
|
||||
index=models.Index(fields=['name'], name='agenda_cult_name_28aa03_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='referencelocation',
|
||||
index=models.Index(fields=['name'], name='agenda_cult_name_76f079_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='staticcontent',
|
||||
index=models.Index(fields=['name'], name='agenda_cult_name_fe4995_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='tag',
|
||||
index=models.Index(fields=['name'], name='agenda_cult_name_9c9c74_idx'),
|
||||
),
|
||||
]
|
@ -0,0 +1,74 @@
|
||||
# Generated by Django 4.2.9 on 2024-11-27 09:00
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django_better_admin_arrayfield.models.fields
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('agenda_culturel', '0118_alter_tag_options_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='tag',
|
||||
options={'verbose_name': 'Tag', 'verbose_name_plural': 'Tags'},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='category',
|
||||
field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.SET_DEFAULT, to='agenda_culturel.category', verbose_name='Category'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='description',
|
||||
field=models.TextField(blank=True, null=True, verbose_name='Description'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='end_day',
|
||||
field=models.DateField(blank=True, null=True, verbose_name='End day'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='end_time',
|
||||
field=models.TimeField(blank=True, null=True, verbose_name='End time'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='exact_location',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='agenda_culturel.place', verbose_name='Location'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='image',
|
||||
field=models.URLField(blank=True, help_text='External URL of the illustration image', max_length=1024, null=True, verbose_name='Illustration (URL)'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='local_image',
|
||||
field=models.ImageField(blank=True, max_length=1024, null=True, upload_to='', verbose_name='Illustration'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='start_day',
|
||||
field=models.DateField(verbose_name='Start day'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='start_time',
|
||||
field=models.TimeField(blank=True, null=True, verbose_name='Start time'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='tags',
|
||||
field=django_better_admin_arrayfield.models.fields.ArrayField(base_field=models.CharField(max_length=64), blank=True, null=True, size=None, verbose_name='Tags'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='event',
|
||||
name='title',
|
||||
field=models.CharField(max_length=512, verbose_name='Title'),
|
||||
),
|
||||
]
|
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.9 on 2024-11-27 18:25
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('agenda_culturel', '0119_alter_tag_options_alter_event_category_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='referencelocation',
|
||||
name='suggested_distance',
|
||||
field=models.IntegerField(default=None, help_text='If this distance is given, this location is part of the suggested filters.', null=True, verbose_name='Suggested distance (km)'),
|
||||
),
|
||||
]
|
@ -0,0 +1,19 @@
|
||||
# Generated by Django 4.2.9 on 2024-11-27 22:13
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('agenda_culturel', '0120_referencelocation_suggested_distance'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='contactmessage',
|
||||
name='related_event',
|
||||
field=models.ForeignKey(default=None, help_text='The message is associated with this event.', null=True, on_delete=django.db.models.deletion.SET_DEFAULT, to='agenda_culturel.event', verbose_name='Related event'),
|
||||
),
|
||||
]
|
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.9 on 2024-11-29 13:44
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('agenda_culturel', '0121_contactmessage_related_event'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='recurrentimport',
|
||||
name='processor',
|
||||
field=models.CharField(choices=[('ical', 'ical'), ('icalnobusy', 'ical no busy'), ('icalnovc', 'ical no VC'), ('lacoope', 'lacoope.org'), ('lacomedie', 'la comédie'), ('lefotomat', 'le fotomat'), ('lapucealoreille', "la puce à l'oreille"), ('Plugin wordpress MEC', 'Plugin wordpress MEC'), ('Facebook events', "Événements d'une page FB"), ('cour3coquins', 'la cour des 3 coquins'), ('arachnee', 'Arachnée concert'), ('rio', 'Le Rio')], default='ical', max_length=20, verbose_name='Processor'),
|
||||
),
|
||||
]
|
@ -0,0 +1,36 @@
|
||||
# Generated by Django 4.2.9 on 2024-11-29 18:18
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('agenda_culturel', '0122_alter_recurrentimport_processor'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='event',
|
||||
name='created_by_user',
|
||||
field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='created_events', to=settings.AUTH_USER_MODEL, verbose_name='Author of the event creation'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='event',
|
||||
name='imported_by_user',
|
||||
field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='imported_events', to=settings.AUTH_USER_MODEL, verbose_name='Author of the last importation'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='event',
|
||||
name='moderated_by_user',
|
||||
field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='moderated_events', to=settings.AUTH_USER_MODEL, verbose_name='Author of the last moderation'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='event',
|
||||
name='modified_by_user',
|
||||
field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='modified_events', to=settings.AUTH_USER_MODEL, verbose_name='Author of the last modification'),
|
||||
),
|
||||
]
|
18
src/agenda_culturel/migrations/0124_place_postcode.py
Normal file
18
src/agenda_culturel/migrations/0124_place_postcode.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.9 on 2024-12-06 21:37
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('agenda_culturel', '0123_event_created_by_user_event_imported_by_user_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='place',
|
||||
name='postcode',
|
||||
field=models.CharField(blank=True, help_text='The post code is not displayed, but makes it easier to find an address when you enter it.', null=True, verbose_name='Postcode'),
|
||||
),
|
||||
]
|
@ -0,0 +1,21 @@
|
||||
# Generated by Django 4.2.9 on 2024-12-11 11:33
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('agenda_culturel', '0124_place_postcode'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameModel(
|
||||
old_name='ContactMessage',
|
||||
new_name='Message',
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='message',
|
||||
options={'verbose_name': 'Message', 'verbose_name_plural': 'Messages'},
|
||||
),
|
||||
]
|
21
src/agenda_culturel/migrations/0126_message_user.py
Normal file
21
src/agenda_culturel/migrations/0126_message_user.py
Normal file
@ -0,0 +1,21 @@
|
||||
# Generated by Django 4.2.9 on 2024-12-11 11:56
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('agenda_culturel', '0125_rename_contactmessage_message_alter_message_options'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='message',
|
||||
name='user',
|
||||
field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.SET_DEFAULT, to=settings.AUTH_USER_MODEL, verbose_name='Author of the message'),
|
||||
),
|
||||
]
|
@ -0,0 +1,25 @@
|
||||
# Generated by Django 4.2.9 on 2024-12-11 19:04
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('agenda_culturel', '0126_message_user'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddIndex(
|
||||
model_name='event',
|
||||
index=models.Index(fields=['end_day', 'end_time'], name='agenda_cult_end_day_4660a5_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='event',
|
||||
index=models.Index(fields=['status'], name='agenda_cult_status_893243_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='event',
|
||||
index=models.Index(fields=['recurrence_dtstart', 'recurrence_dtend'], name='agenda_cult_recurre_a8911c_idx'),
|
||||
),
|
||||
]
|
18
src/agenda_culturel/migrations/0128_event_datetimes_title.py
Normal file
18
src/agenda_culturel/migrations/0128_event_datetimes_title.py
Normal file
@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.9 on 2024-12-11 19:12
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.functions.text
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('agenda_culturel', '0127_event_agenda_cult_end_day_4660a5_idx_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddIndex(
|
||||
model_name='event',
|
||||
index=models.Index(models.F('start_time'), models.F('start_day'), models.F('end_day'), models.F('end_time'), django.db.models.functions.text.Lower('title'), name='datetimes title'),
|
||||
),
|
||||
]
|
@ -0,0 +1,57 @@
|
||||
# Generated by Django 4.2.9 on 2024-12-11 19:58
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('agenda_culturel', '0128_event_datetimes_title'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddIndex(
|
||||
model_name='batchimportation',
|
||||
index=models.Index(fields=['created_date'], name='agenda_cult_created_a23990_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='batchimportation',
|
||||
index=models.Index(fields=['status'], name='agenda_cult_status_54b205_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='batchimportation',
|
||||
index=models.Index(fields=['created_date', 'recurrentImport'], name='agenda_cult_created_0296e4_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='duplicatedevents',
|
||||
index=models.Index(fields=['representative'], name='agenda_cult_represe_9a4fa2_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='message',
|
||||
index=models.Index(fields=['related_event'], name='agenda_cult_related_79de3c_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='message',
|
||||
index=models.Index(fields=['user'], name='agenda_cult_user_id_42dc88_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='message',
|
||||
index=models.Index(fields=['date'], name='agenda_cult_date_049c71_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='message',
|
||||
index=models.Index(fields=['spam', 'closed'], name='agenda_cult_spam_22f9b3_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='place',
|
||||
index=models.Index(fields=['name'], name='agenda_cult_name_222846_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='place',
|
||||
index=models.Index(fields=['city'], name='agenda_cult_city_156dc7_idx'),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name='place',
|
||||
index=models.Index(fields=['location'], name='agenda_cult_locatio_6f3c05_idx'),
|
||||
),
|
||||
]
|
File diff suppressed because it is too large
Load Diff
@ -14,7 +14,8 @@ APP_ENV = os_getenv("APP_ENV", "dev")
|
||||
DEBUG = os_getenv("DEBUG", "true").lower() in ["True", "true", "1", "yes", "y"]
|
||||
|
||||
ALLOWED_HOSTS = os_getenv("ALLOWED_HOSTS", "localhost").split(",")
|
||||
|
||||
if DEBUG:
|
||||
ALLOWED_HOSTS = ALLOWED_HOSTS + ['testserver']
|
||||
|
||||
if DEBUG:
|
||||
CSRF_TRUSTED_ORIGINS = os_getenv("CSRF_TRUSTED_ORIGINS", "http://localhost").split(
|
||||
@ -55,9 +56,10 @@ INSTALLED_APPS = [
|
||||
"robots",
|
||||
"debug_toolbar",
|
||||
"cache_cleaner",
|
||||
"honeypot",
|
||||
]
|
||||
|
||||
SITE_ID = 1
|
||||
HONEYPOT_FIELD_NAME = "alias_name"
|
||||
|
||||
MIDDLEWARE = [
|
||||
"django.middleware.security.SecurityMiddleware",
|
||||
@ -71,6 +73,7 @@ MIDDLEWARE = [
|
||||
"django.contrib.messages.middleware.MessageMiddleware",
|
||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||
"debug_toolbar.middleware.DebugToolbarMiddleware",
|
||||
'django.contrib.sites.middleware.CurrentSiteMiddleware',
|
||||
# "django.middleware.cache.UpdateCacheMiddleware",
|
||||
# "django.middleware.common.CommonMiddleware",
|
||||
# "django.middleware.cache.FetchFromCacheMiddleware",
|
||||
@ -144,10 +147,9 @@ TIME_ZONE = "Europe/Paris"
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_TZ = True
|
||||
USE_TZ = False
|
||||
|
||||
LANGUAGES = (
|
||||
("en-us", _("English")),
|
||||
("fr", _("French")),
|
||||
)
|
||||
|
||||
|
13
src/agenda_culturel/sitemaps.py
Normal file
13
src/agenda_culturel/sitemaps.py
Normal file
@ -0,0 +1,13 @@
|
||||
from django.contrib import sitemaps
|
||||
from django.urls import reverse
|
||||
|
||||
|
||||
class StaticViewSitemap(sitemaps.Sitemap):
|
||||
priority = 0.5
|
||||
changefreq = "daily"
|
||||
|
||||
def items(self):
|
||||
return ["home", "cette_semaine", "ce_mois_ci", "aujourdhui", "a_venir", "about", "contact"]
|
||||
|
||||
def location(self, item):
|
||||
return reverse(item)
|
File diff suppressed because one or more lines are too long
589
src/agenda_culturel/static/images/alunissage.svg
Normal file
589
src/agenda_culturel/static/images/alunissage.svg
Normal file
@ -0,0 +1,589 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="196.25674mm"
|
||||
height="195.90508mm"
|
||||
viewBox="0 0 196.25674 195.90508"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
xml:space="preserve"
|
||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||
sodipodi:docname="alunissage.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.67644895"
|
||||
inkscape:cx="338.53257"
|
||||
inkscape:cy="476.75438"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1020"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1"
|
||||
showguides="true"><sodipodi:guide
|
||||
position="87.392073,277.9802"
|
||||
orientation="0,-1"
|
||||
id="guide14594"
|
||||
inkscape:locked="false" /></sodipodi:namedview><defs
|
||||
id="defs2"><inkscape:perspective
|
||||
sodipodi:type="inkscape:persp3d"
|
||||
inkscape:vp_x="0 : 95.972889 : 1"
|
||||
inkscape:vp_y="0 : 999.99997 : 0"
|
||||
inkscape:vp_z="210.00001 : 47.405071 : 1"
|
||||
inkscape:persp3d-origin="105 : -2.0949277 : 1"
|
||||
id="perspective240" /><mask
|
||||
maskUnits="userSpaceOnUse"
|
||||
id="mask15432"><rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect15434"
|
||||
width="178.99583"
|
||||
height="178.99583"
|
||||
x="-2.2718451"
|
||||
y="12.442876"
|
||||
ry="12.835461"
|
||||
transform="rotate(-6.4517442)" /></mask><mask
|
||||
maskUnits="userSpaceOnUse"
|
||||
id="mask15436"><rect
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect15438"
|
||||
width="178.99583"
|
||||
height="178.99583"
|
||||
x="-2.2718451"
|
||||
y="12.442876"
|
||||
ry="12.835461"
|
||||
transform="rotate(-6.4517442)" /></mask></defs><g
|
||||
inkscape:label="Calque 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-6.8716276,-21.840211)"><rect
|
||||
style="fill:#003737;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect14856"
|
||||
width="253.23183"
|
||||
height="167.6301"
|
||||
x="-24.006699"
|
||||
y="-10.841731"
|
||||
ry="12.835461"
|
||||
mask="url(#mask15436)"
|
||||
transform="rotate(11.86311,-35.469528,138.62496)" /><path
|
||||
sodipodi:type="star"
|
||||
style="fill:#ffcc00;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path15414-7"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="117.51744"
|
||||
sodipodi:cy="29.413408"
|
||||
sodipodi:r1="11.625795"
|
||||
sodipodi:r2="5.8128977"
|
||||
sodipodi:arg1="-0.43776009"
|
||||
sodipodi:arg2="0.19055844"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m 128.04696,24.485096 -4.82184,6.029317 2.23323,7.390235 -7.22426,-2.722688 -6.33842,4.407635 0.35701,-7.712031 -6.15059,-4.666166 7.4449,-2.043609 2.53715,-7.291484 4.24419,6.44901 z"
|
||||
inkscape:transform-center-x="-0.079786515"
|
||||
inkscape:transform-center-y="-0.21888701"
|
||||
transform="matrix(0.26075369,0.0547741,-0.0547741,0.26075369,114.71036,76.196413)" /><path
|
||||
id="path14500"
|
||||
mask="url(#mask15432)"
|
||||
style="fill:#c5beb2;fill-opacity:1;stroke:none;stroke-linecap:round;stroke-linejoin:round"
|
||||
d="m 52.195264,77.951831 c -0.0995,-0.0019 -0.200891,0.01327 -0.300757,0.04754 -0.456533,0.156667 -0.698235,0.650377 -0.541569,1.10691 l 2.971911,8.660453 c -3.387288,2.212886 -7.162412,5.308918 -10.404532,3.340364 -1.529299,-0.928561 -15.94685,17.177302 -17.573605,17.823722 -32.2185841,12.80254 -53.107353,33.35874 -53.107352,56.53505 2e-6,38.81626 58.59402,70.28305 130.87366,70.28305 72.27965,0 130.87419,-31.46679 130.87419,-70.28305 0,-38.81626 -59.1882,-79.527878 -130.87419,-70.283051 -23.606895,3.044413 -33.441303,-5.838959 -44.427257,-9.480042 -0.903167,-0.299337 -2.018561,2.77e-4 -3.188953,0.712101 l -0.656291,0.399459 -2.839103,-8.273397 c -0.122395,-0.356666 -0.450783,-0.582285 -0.806152,-0.589111 z"
|
||||
transform="rotate(11.86311,-33.77711,138.8008)" /><g
|
||||
id="g1576"
|
||||
style="stroke-width:0.6;stroke-dasharray:none"
|
||||
transform="rotate(11.86311,105,119.79275)"><path
|
||||
style="fill:#fff7e7;fill-opacity:0.55895;stroke:none;stroke-width:0.6;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 102.8368,132.68366 c 1.03176,3.55726 17.34099,3.88136 17.73538,2.3582 0.39438,-1.52316 -1.52847,-2.36494 -1.52847,-2.36494 -1.08228,3.21929 -7.28172,2.38299 -13.21987,2.03963 z"
|
||||
id="path1162-6"
|
||||
sodipodi:nodetypes="csccc" /><path
|
||||
style="fill:#ffffff;fill-opacity:0.294988;stroke:none;stroke-width:0.6;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 102.6432,132.68366 c -0.93632,0.70147 0.77648,-0.0567 -1.60327,0.4643 1.39003,2.05537 5.21589,1.69949 5.99161,2.3598 l -3.68801,-1.88107 z"
|
||||
id="path1533"
|
||||
sodipodi:nodetypes="ccccc" /><path
|
||||
style="fill:#000000;fill-opacity:0.294988;stroke:none;stroke-width:0.6;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 120.6255,133.36309 c 1.54184,3.26173 -10.17172,2.70066 -13.59396,2.14467 8.66787,2.8241 15.90667,3.7079 17.91461,-0.53782 -2.54611,-0.0305 -3.04763,-0.99487 -4.32065,-1.60685 z"
|
||||
id="path1533-7"
|
||||
sodipodi:nodetypes="cccc" /><path
|
||||
style="fill:none;stroke:#000000;stroke-width:0.6;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 102.6432,132.68366 c 1.03176,3.55726 17.34099,3.88136 17.73538,2.3582 0.39438,-1.52316 -1.52847,-2.36494 -1.52847,-2.36494"
|
||||
id="path1162" /></g><g
|
||||
id="g1576-5"
|
||||
transform="matrix(0.51059462,0.20026137,-0.21398251,0.68212303,154.90211,50.373061)"
|
||||
style="stroke-width:0.679551;stroke-dasharray:none"><path
|
||||
style="fill:#fff7e7;fill-opacity:0.55895;stroke:none;stroke-width:0.679551;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 103.34353,133.62669 c 1.03176,3.55726 16.83426,2.93833 17.22865,1.41517 0.39438,-1.52316 -1.52847,-2.36494 -1.52847,-2.36494 -0.19099,0.56812 -1.44864,0.34803 -1.9288,0.68854 -2.24071,1.58902 -6.40085,1.63386 -11.29107,1.35109 z"
|
||||
id="path1162-6-3"
|
||||
sodipodi:nodetypes="cscscc" /><path
|
||||
style="fill:#ffffff;fill-opacity:0.294988;stroke:none;stroke-width:0.679551;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 103.34353,133.62669 c -0.93632,0.70147 0.77648,-0.0567 -1.60327,0.4643 1.39003,2.05537 4.51556,0.75646 5.29128,1.41677 z"
|
||||
id="path1533-5"
|
||||
sodipodi:nodetypes="ccccc" /><path
|
||||
style="fill:#000000;fill-opacity:0.294988;stroke:none;stroke-width:0.679551;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 119.62517,132.53874 c 4.06792,4.14821 -10.28515,2.76677 -13.80133,2.17781 8.66787,2.8241 18.39394,5.38385 20.40188,1.13813 -3.61173,-2.57319 -5.4227,-2.93647 -6.60055,-3.31594 z"
|
||||
id="path1533-7-6"
|
||||
sodipodi:nodetypes="cccc" /><path
|
||||
style="fill:none;stroke:#000000;stroke-width:0.679551;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 106.64826,132.51145 c -11.319438,2.14423 12.45226,3.77801 13.73032,2.53041 3.06792,-2.99478 -5.8967,-3.56684 -6.68296,-3.13405"
|
||||
id="path1162-2"
|
||||
sodipodi:nodetypes="csc" /></g><g
|
||||
id="g1576-9"
|
||||
transform="matrix(1.9425519,0.40805381,-0.39033153,1.8581845,-7.6916871,-95.795008)"
|
||||
style="stroke-width:0.390962;stroke-dasharray:none"><path
|
||||
style="fill:#fff7e7;fill-opacity:0.55895;stroke:none;stroke-width:0.390962;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 102.8368,132.68366 c 3.10079,2.69207 17.34099,3.88136 17.73538,2.3582 0.39438,-1.52316 -0.91154,-1.8437 -0.91154,-1.8437 -1.81421,2.61261 -7.77833,1.56442 -13.71648,1.22106 z"
|
||||
id="path1162-6-1"
|
||||
sodipodi:nodetypes="csccc" /><path
|
||||
style="fill:#ffffff;fill-opacity:0.294988;stroke:none;stroke-width:0.390962;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 102.6432,132.68366 c -0.93632,0.70147 0.77648,-0.0567 -1.60327,0.4643 1.39003,2.05537 5.38317,0.9933 6.15889,1.65361 l -3.85529,-1.17488 z"
|
||||
id="path1533-2"
|
||||
sodipodi:nodetypes="ccccc" /><path
|
||||
style="fill:#000000;fill-opacity:0.294988;stroke:none;stroke-width:0.390962;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 119.79645,132.67692 c 3.69728,4.17523 -9.13801,2.69648 -12.59763,2.12465 0.36908,0.83812 13.1969,4.98485 16.72321,1.10249 -0.33987,-0.41869 -2.85256,-2.61516 -4.12558,-3.22714 z"
|
||||
id="path1533-7-7"
|
||||
sodipodi:nodetypes="cccc" /><path
|
||||
style="fill:none;stroke:#000000;stroke-width:0.390962;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 106.05918,131.55532 c -1.77289,-0.20918 -3.71225,0.78168 -3.22238,1.1286 3.42679,2.42499 17.19672,3.69085 17.54178,2.3582 0.21258,-0.82098 -0.24806,-1.44401 -0.71794,-1.84396"
|
||||
id="path1162-0"
|
||||
sodipodi:nodetypes="cssc" /></g><g
|
||||
id="g1576-9-9"
|
||||
transform="matrix(0.84888437,-0.29780159,0.16736444,0.96227663,-93.058232,39.699665)"
|
||||
style="stroke-width:0.462959;stroke-dasharray:none"><path
|
||||
style="fill:#fff7e7;fill-opacity:0.55895;stroke:none;stroke-width:0.462959;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 102.8368,132.68366 c 1.03176,3.55726 17.34099,3.88136 17.73538,2.3582 0.39438,-1.52316 -0.95533,-1.64385 -0.95533,-1.64385 -1.08228,3.21929 -7.85486,1.6619 -13.79301,1.31854 z"
|
||||
id="path1162-6-1-3"
|
||||
sodipodi:nodetypes="csccc" /><path
|
||||
style="fill:#000000;fill-opacity:0.294988;stroke:none;stroke-width:0.462959;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 120.6255,133.36309 c 1.54184,3.26173 -10.20493,2.23076 -13.62717,1.67477 8.66787,2.8241 15.08072,5.33692 17.08866,1.0912 -1.40447,-0.83726 -2.18847,-2.15399 -3.46149,-2.76597 z"
|
||||
id="path1533-7-7-0"
|
||||
sodipodi:nodetypes="cccc" /><path
|
||||
style="fill:none;stroke:#000000;stroke-width:0.462959;stroke-linecap:round;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 104.98821,132.30093 c -2.97484,-0.16805 -2.07389,0.81205 -1.79877,1.01131 3.05188,2.21032 16.82736,3.12682 17.18914,1.72962 0.14178,-0.54755 -0.0159,-1.00705 -0.27606,-1.37105"
|
||||
id="path1162-0-6"
|
||||
sodipodi:nodetypes="cssc" /></g><g
|
||||
id="g13683"
|
||||
transform="matrix(1.1100761,0.33327025,-0.33327025,1.1100761,40.007817,-13.329961)"
|
||||
style="stroke-width:0.999979;stroke-dasharray:none"><path
|
||||
style="fill:#010000;fill-opacity:0.254893;stroke:none;stroke-width:0.707892;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 154.14006,144.23649 c 0.96552,4.45831 37.8521,2.93154 43.31493,0.19171 13.87936,-2.9076 -21.07506,-6.50284 -27.9251,-6.90834 -4.61395,0.0322 -15.93897,3.74541 -15.38983,6.71663 z"
|
||||
id="path38464-7-3"
|
||||
sodipodi:nodetypes="cccc"
|
||||
transform="matrix(1.4126153,0,0,1.4126153,-123.1007,-68.10763)" /><path
|
||||
style="fill:#010000;fill-opacity:0.254893;stroke:none;stroke-width:0.999979;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 101.41768,142.86865 c 0.56665,2.63741 20.69879,2.10306 23.65815,0.54228 7.55455,-1.57625 -11.56752,-4.04179 -15.31224,-4.34869 -2.51963,-0.0268 -8.67203,2.04966 -8.34591,3.80641 z"
|
||||
id="path38464-7-3-1"
|
||||
sodipodi:nodetypes="cccc" /><path
|
||||
style="fill:#fffaed;fill-opacity:1;stroke:#2d002d;stroke-width:0.999979;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 120.30447,118.03891 -2.84964,9.16842 11.39857,0.61949 3.09743,-0.61949 -1.85846,-10.53129 z"
|
||||
id="path4251" /><rect
|
||||
style="fill:#ded6c3;fill-opacity:1;stroke:#2d002d;stroke-width:0.999979;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect611"
|
||||
width="2.1245613"
|
||||
height="30.266191"
|
||||
x="138.8942"
|
||||
y="56.15984"
|
||||
ry="0.68848425"
|
||||
transform="rotate(23.593176)" /><path
|
||||
id="rect611-5"
|
||||
style="fill:#e3c56d;fill-opacity:1;stroke:#2d002d;stroke-width:0.999979;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
|
||||
d="m 96.397714,128.03026 0.685099,0.29921 c 0.349537,0.15266 0.508031,0.55695 0.355371,0.90649 l -2.798277,6.40709 c -0.15266,0.34953 -4.707521,1.9948 -5.057058,1.84214 l -0.6851,-0.29921 c -0.349537,-0.15266 -0.67203,-0.69387 -0.355371,-0.90649 l 4.953966,-3.32627 1.994881,-4.56759 c 0.152659,-0.34954 0.556951,-0.50803 0.906489,-0.35537 z"
|
||||
sodipodi:nodetypes="ssssssscss" /><rect
|
||||
style="fill:#969184;fill-opacity:1;stroke:#2d002d;stroke-width:0.999979;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect611-3-6"
|
||||
width="1.5656743"
|
||||
height="26.733509"
|
||||
x="-82.484116"
|
||||
y="149.62541"
|
||||
ry="0.68848425"
|
||||
transform="matrix(-0.91641041,0.40023989,0.40023989,0.91641041,0,0)" /><rect
|
||||
style="fill:#c2bbaa;fill-opacity:1;stroke:#2d002d;stroke-width:0.999979;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect611-3-6-7"
|
||||
width="1.9859811"
|
||||
height="27.541084"
|
||||
x="147.46117"
|
||||
y="46.883339"
|
||||
ry="0.68848425"
|
||||
transform="rotate(23.593176)" /><rect
|
||||
style="fill:#ded6c3;fill-opacity:1;stroke:#2d002d;stroke-width:0.999979;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect611-3"
|
||||
width="2.1245613"
|
||||
height="30.266191"
|
||||
x="-90.528412"
|
||||
y="155.31551"
|
||||
ry="0.68848425"
|
||||
transform="matrix(-0.91641041,0.40023989,0.40023989,0.91641041,0,0)" /><path
|
||||
style="fill:#fffaed;fill-opacity:1;stroke:#2d002d;stroke-width:0.999979;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 125.74289,77.772227 -13.47406,5.823183 1.85846,8.672825 11.79229,0.697766 z"
|
||||
id="path4138"
|
||||
sodipodi:nodetypes="ccccc" /><path
|
||||
style="fill:#99978f;fill-opacity:1;stroke:#2d002d;stroke-width:0.999979;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 135.31376,80.374076 -9.57087,-2.601849 -2.51485,14.293188 11.83793,1.441795 z"
|
||||
id="path4138-5"
|
||||
sodipodi:nodetypes="ccccc" /><path
|
||||
style="fill:#fffaed;fill-opacity:1;stroke:#2d002d;stroke-width:0.999979;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 105.09002,106.45299 7.6744,-3.15788 6.19488,-0.37169 2.08204,-10.246073 9.75647,0.577307 1.97857,10.807906 8.74996,-0.78515 4.21251,2.40094 -1.68214,0.43101 -9.50403,14.6199 -23.12458,0.14575 z"
|
||||
id="path4148"
|
||||
sodipodi:nodetypes="cccccccccccc" /><path
|
||||
style="fill:#43423e;fill-opacity:1;stroke:none;stroke-width:0.999979;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 115.05652,103.47421 -2.61954,1.27227 6.03364,1.0729 10.80028,0.28998 8.31727,-0.5 3.1935,-1.8063 -8.29002,0.63247 -0.60277,-4.08554 -10.78233,-1.921271 z"
|
||||
id="path5886"
|
||||
sodipodi:nodetypes="cccccccccc" /><path
|
||||
style="fill:#c9c5ba;fill-opacity:1;stroke:#2d002d;stroke-width:0.999979;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 129.2709,97.103982 -5.22158,4.319628 -2.94277,-2.994889 1.62836,-8.44273 2.95585,-3.079736"
|
||||
id="path4146"
|
||||
sodipodi:nodetypes="ccccc" /><path
|
||||
style="fill:#a29f96;fill-opacity:1;stroke:#2d002d;stroke-width:0.999979;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 141.17794,101.27836 -9.8941,1.84077 -7.23452,-1.69552 4.64514,-3.842756"
|
||||
id="path4146-6"
|
||||
sodipodi:nodetypes="cccc" /><path
|
||||
style="fill:#fffaed;fill-opacity:1;stroke:#2d002d;stroke-width:0.999979;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 127.67311,98.800416 13.50483,2.477954 3.46913,-4.832008 -6.81436,-9.787903 c 0,0 -5.94708,-2.354053 -6.31877,-1.982359 -0.37169,0.371692 -5.82318,2.230155 -5.82318,2.230155 l -0.1239,8.672824 z"
|
||||
id="path751" /><path
|
||||
style="fill:#d6d2c7;fill-opacity:1;stroke:#2d002d;stroke-width:0.999979;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 106.50828,94.250596 4.27378,-3.978166 h 2.23016 l 8.02912,2.404919 1.75878,3.060015 c 0,0 0.24779,4.336416 -0.1239,4.212516 -0.37169,-0.123897 -3.71692,2.97354 -3.71692,2.97354 l -6.19488,0.37169 c 0,0 -2.84964,0.74339 -3.22134,0.61949 -0.37169,-0.1239 -3.0348,-3.96472 -3.0348,-3.96472 z"
|
||||
id="path4140" /><path
|
||||
style="fill:#96938b;fill-opacity:1;stroke:#2d002d;stroke-width:0.999979;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 111.76884,94.583154 11.03128,1.57438 -0.1239,4.212506 -3.71692,2.97354 -7.80555,0.26127 z"
|
||||
id="path4140-7"
|
||||
sodipodi:nodetypes="cccccc" /><path
|
||||
style="fill:#fffaed;fill-opacity:1;stroke:#2d002d;stroke-width:0.999979;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 106.50828,94.250596 4.27378,-3.978166 h 2.23016 l 3.44263,4.764064 -0.68593,4.733009 -3.0045,3.525607 -3.22134,0.61949 -3.0348,-3.96472 z"
|
||||
id="path4140-2"
|
||||
sodipodi:nodetypes="ccccccccc" /><path
|
||||
style="fill:#d4aa00;fill-opacity:1;stroke:#2d002d;stroke-width:0.999979;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 135.44739,107.04067 8.60932,-0.93131 -0.64536,13.81457 -8.85867,0.80533 z"
|
||||
id="path4142" /><path
|
||||
style="fill:#d4aa00;fill-opacity:1;stroke:#2d002d;stroke-width:0.999979;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 105.09002,106.45299 9.37357,-0.6336 0.80533,15.05562 h -7.68164 z"
|
||||
id="path4144" /><rect
|
||||
style="fill:#84827b;fill-opacity:1;stroke:#2d002d;stroke-width:0.999979;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="rect4379"
|
||||
width="15.495975"
|
||||
height="7.5343547"
|
||||
x="116.95485"
|
||||
y="111.96205"
|
||||
ry="0.68848425" /><path
|
||||
id="rect611-5-3-2"
|
||||
style="fill:#e3c56d;fill-opacity:1;stroke:#2d002d;stroke-width:0.999979;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
|
||||
d="m 143.65023,123.32624 -0.6851,0.29921 c -0.34954,0.15266 -0.50965,0.55766 -0.35537,0.90649 l 1.94251,4.39209 c -0.1135,0.3747 3.00822,0.0602 1.14357,-2.68983 l -1.13913,-2.55259 c -0.15544,-0.34831 -0.55695,-0.50802 -0.90648,-0.35537 z"
|
||||
sodipodi:nodetypes="sssccss" /><path
|
||||
id="rect611-5-3-2-1"
|
||||
style="fill:#e3c56d;fill-opacity:1;stroke:#2d002d;stroke-width:0.999979;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
|
||||
d="m 108.24636,122.39419 0.6851,0.29921 c 0.34954,0.15266 0.50965,0.55766 0.35537,0.90649 l -1.94251,4.39209 c 0.1135,0.3747 -3.00822,0.0602 -1.14357,-2.68983 l 1.13913,-2.55259 c 0.15544,-0.34831 0.55695,-0.50802 0.90648,-0.35537 z"
|
||||
sodipodi:nodetypes="sssccss" /><g
|
||||
id="g11682"
|
||||
transform="translate(68.350363,-11.452931)"
|
||||
style="stroke-width:0.999979;stroke-dasharray:none"><g
|
||||
id="g11781"
|
||||
transform="matrix(-0.01139951,0.54218684,-0.54218684,-0.01139951,138.28173,106.48398)"
|
||||
style="stroke-width:1.84394;stroke-dasharray:none"><path
|
||||
style="fill:#eb4402;fill-opacity:1;stroke:#000000;stroke-width:1.84394;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 71.402761,191.27378 c -2.113043,0.27054 -16.51583,0.34774 -21.68703,-7.93835 -5.171192,-8.2861 -3.76397,-13.58169 -2.23407,-17.59746 1.52989,-4.01577 2.90744,-5.92547 5.80687,-8.7927 2.89944,-2.86723 5.99845,-8.59829 11.9153,-8.91316 5.91685,-0.31487 13.24668,1.03438 18.14476,6.51896 15.030422,13.3405 3.49522,36.12115 -11.94583,36.72271 z"
|
||||
id="path38521"
|
||||
sodipodi:nodetypes="czzzzcc" /><path
|
||||
style="fill:none;stroke:#000000;stroke-width:1.84394;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 52.045331,168.07345 c 0,0 4.20442,-1.16049 5.95753,-1.68797 0.53786,-0.16183 0.79434,-3.57452 0.69505,-3.97169 -0.0993,-0.39717 -1.09221,-2.4823 -1.09221,-2.4823"
|
||||
id="path39627"
|
||||
sodipodi:nodetypes="cszc" /><path
|
||||
style="fill:#510701;fill-opacity:1;stroke:#000000;stroke-width:1.84394;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 57.857211,165.65634 c -0.10531,-0.14042 -1.84121,-1.73181 -2.63288,-2.7733 -0.79167,-1.04149 -1.69399,-2.52693 -2.0712,-3.45155 -0.37721,-0.92462 -0.73899,-1.48616 -0.45636,-1.91264 0.28262,-0.42648 0.73423,-0.30422 1.08825,-0.18241 0.35403,0.12181 0.68488,0.55237 0.77231,0.84252 0.0874,0.29015 -0.11967,0.3973 -0.10531,0.667 0.0144,0.2697 -0.0456,0.27495 0.24573,0.91273 0.29133,0.63778 1.16007,1.94648 1.82547,2.70309 0.6654,0.75661 1.8591,1.04674 2.08899,1.83989 0.22989,0.79315 -0.0927,2.0968 -0.60935,2.08381 -0.51669,-0.013 -0.21586,-0.90466 -0.21586,-0.90466"
|
||||
id="path39629"
|
||||
sodipodi:nodetypes="czzzzzzzzzzc" /><path
|
||||
style="fill:#006060;fill-opacity:1;stroke:#000000;stroke-width:1.84394;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 54.017211,161.49841 c -8.81401,3.40711 -10.967102,13.7965 -9.431327,18.16737 1.535777,4.37087 2.112697,8.01053 6.643607,9.05239 -1.87367,-6.18344 -0.6994,-9.06026 -0.24737,-13.55754 0.46657,-4.6417 3.29718,-7.78272 3.03509,-13.66222"
|
||||
id="path1596-0"
|
||||
sodipodi:nodetypes="czcac" /></g></g><path
|
||||
id="rect611-5-3"
|
||||
style="fill:#e3c56d;fill-opacity:1;stroke:#2d002d;stroke-width:0.999979;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none"
|
||||
d="m 154.06944,128.30107 -0.6851,0.29921 c -0.34954,0.15266 -0.50803,0.55695 -0.35537,0.90649 l 2.79827,6.40709 c 0.15266,0.34953 4.70752,1.9948 5.05706,1.84214 l 0.6851,-0.29921 c 0.34954,-0.15266 0.67203,-0.69387 0.35537,-0.90649 l -4.95396,-3.32627 -1.99489,-4.56759 c -0.15265,-0.34954 -0.55695,-0.50803 -0.90648,-0.35537 z"
|
||||
sodipodi:nodetypes="ssssssscss" /><path
|
||||
style="fill:#010000;fill-opacity:0.0677805;stroke:none;stroke-width:0.999979;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:0.448687"
|
||||
d="m 55.100582,93.762192 c 2.207116,5.124587 9.321543,8.525138 5.634754,17.629658 9.491228,-1.65632 17.957048,-1.89985 23.547213,-12.53699 -7.75868,1.85165 -15.669366,-1.119461 -22.122416,-2.661586 -3.017395,-0.721085 -6.271335,-2.616591 -7.059551,-2.431082 z"
|
||||
id="path1056"
|
||||
sodipodi:nodetypes="cccsc" /></g><g
|
||||
id="g13651"
|
||||
transform="matrix(1.1630717,-0.1292531,0.1292531,1.1630717,-24.892917,-1.9386015)"><path
|
||||
style="fill:#008989;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 42.665935,137.70912 c -1.194356,-3.34818 -2.12459,-5.15105 -4.19392,-6.18037 -3.842759,-1.91146 -7.019594,-1.96348 -11.288621,-1.52093 -2.496518,0.2588 -5.02284,1.69478 -6.752563,3.33155 4.475795,1.38898 6.045382,3.25862 9.364751,3.91095 4.212272,0.82782 8.580238,0.30587 12.870353,0.4588"
|
||||
id="path1596"
|
||||
sodipodi:nodetypes="cccccc" /><path
|
||||
style="fill:#010000;fill-opacity:0.268258;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 49.779293,186.08404 c 0.96552,4.45831 25.01987,3.96877 30.4827,1.22894 13.87936,-2.9076 -16.08645,-5.72863 -22.93649,-6.13413 -4.61395,0.0322 -8.09535,1.93397 -7.54621,4.90519 z"
|
||||
id="path38464-7"
|
||||
sodipodi:nodetypes="cccc"
|
||||
transform="matrix(1.036102,0,0,1.036102,-21.713571,-27.542843)" /><path
|
||||
style="fill:#a30b0e;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 33.983044,165.18272 c 0,0 1.529564,0.72114 2.455335,0.77196 0.925764,0.0508 1.834838,-0.45446 2.932539,-0.57897 1.097708,-0.1245 2.418383,-0.24077 3.637575,-0.11265 1.219185,0.12811 1.952311,1.0547 3.609267,0.88462 1.656956,-0.17008 3.491919,-0.39037 5.666685,-2.36448 2.174767,-1.9741 4.865746,-7.29359 5.848185,-10.05882 0.982432,-2.76524 0.703154,-5.07155 0.643539,-6.39325 -0.05959,-1.3217 -0.208211,-1.49572 -0.208211,-1.49572"
|
||||
id="path6267"
|
||||
sodipodi:nodetypes="czzzzzzzc" /><path
|
||||
style="fill:#c07f16;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 38.461151,136.9473 c 1.35573,0.37324 2.309562,0.8613 4.096886,1.11122 0.994124,-0.15196 1.841666,-0.0987 3.149379,-0.68969 l -0.995306,6.88319 c 0,0 -4.907344,0.0713 -4.771272,-0.3995 0.136064,-0.47077 -1.479687,-6.90522 -1.479687,-6.90522 z"
|
||||
id="path24930"
|
||||
sodipodi:nodetypes="ccccsc" /><path
|
||||
style="fill:#f1e890;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 43.034803,142.60286 c -0.935713,-2.59107 -3.812328,-6.78313 -7.729574,-6.24009 -3.917239,0.54304 -7.35144,5.76415 -7.971266,11.25782 -0.619818,5.49367 5.398984,17.75754 7.434345,17.89209 2.355997,0.40404 4.34644,-1.17116 4.886578,-1.87052 0.540145,-0.69936 0.768947,1.03528 1.371544,1.15912 0.602598,0.12384 1.077606,-0.73571 1.812803,-0.55339 0.735197,0.18233 2.932036,1.63625 4.414655,0.73425 6.133481,-3.8826 11.61906,-11.23177 11.344545,-18.37487 -0.167597,-4.36102 -3.17722,-9.92296 -7.46061,-10.7591 -5.082506,-0.99215 -6.805686,2.91188 -8.10302,6.75469 z"
|
||||
id="path6257"
|
||||
sodipodi:nodetypes="czzczzzcssc" /><path
|
||||
style="fill:#744e0b;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 42.473921,135.50687 -0.310153,-1.00963 0.689003,-0.35083 0.613684,0.15782 0.04829,3.81859 v 2.48554 c -0.267731,0.17111 -0.618885,0.0114 -0.964969,-0.1281 -2.2e-5,-0.0968 -0.07585,-4.97339 -0.07585,-4.97339 z"
|
||||
id="path6259"
|
||||
sodipodi:nodetypes="cccccccc" /><path
|
||||
style="fill:#60140f;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 46.582777,147.89717 c -0.07872,0.32449 -0.06696,0.58859 -0.03684,0.95049 0.03013,0.36191 -0.201055,1.05388 0.249846,1.20205 0.450901,0.14817 1.057495,-0.43819 1.206215,-1.08137 0.14872,-0.64317 -0.926857,-2.09678 -0.926857,-2.09678 0,0 -0.413643,0.70112 -0.492369,1.02561 z"
|
||||
id="path6261"
|
||||
sodipodi:nodetypes="zzzzcz" /><path
|
||||
style="fill:#60140f;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 38.823632,147.137 c -0.387631,0.0217 -0.158494,0.22509 -0.289482,0.55826 -0.130995,0.33317 -0.62437,1.08311 -0.406436,1.46373 0.217941,0.38061 0.737903,0.41781 1.075111,0.2859 0.337208,-0.13192 0.573508,-0.4352 0.567666,-0.89527 -0.0058,-0.46008 -0.559227,-1.43438 -0.946859,-1.41262 z"
|
||||
id="path6263"
|
||||
sodipodi:nodetypes="zzzzzz" /><path
|
||||
style="fill:#f5dc0c;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 42.742754,144.31587 -0.592736,3.33348 -0.231085,5.08667 0.729398,2.86364 0.550576,-3.43708 -0.02801,-2.59306 z"
|
||||
id="path6265" /><path
|
||||
style="fill:#ff6600;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 63.571721,126.76017 c 1.094536,11.13944 0.818311,21.64168 -1.928813,33.00992 0,0 3.528354,-4.80953 3.519073,-2.8943 -0.02658,5.48412 2.630888,-29.5498 1.591832,-29.40137 z"
|
||||
id="path3853"
|
||||
sodipodi:nodetypes="ccscc" /><path
|
||||
d="m 57.958721,157.97494 c 0,0 -0.355132,-4.22053 2.994472,-5.6209 0,0 3.297109,-0.26031 3.603353,0.72059 0,0 3.320156,-0.85575 3.303628,1.60641 0,0 0.195132,0.99026 -0.655974,1.50929 0,0 2.073953,0.72031 0.890684,2.60951 l -1.146963,0.99133 c 0,0 1.14913,1.69296 -0.90507,2.53692 0,0 -0.619668,0.61141 -1.665989,0.14018 0,0 -2.66452,1.56645 -4.971036,-0.58839 0,0 -1.499942,-0.54491 -1.447105,-3.90494"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.729413;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path2902" /><path
|
||||
d="m 57.958721,157.97494 c 0,0 -0.355132,-4.22053 2.994472,-5.6209 0,0 3.231666,-0.34283 3.53791,0.63807 0,0 3.385599,-0.77323 3.369071,1.68893 0,0 0.195132,0.99026 -0.655974,1.50929 0,0 2.073953,0.72031 0.890684,2.60951 l -1.146963,0.99133 c 0,0 1.14913,1.69296 -0.90507,2.53692 0,0 -0.619668,0.61141 -1.665989,0.14018 0,0 -2.66452,1.56645 -4.971036,-0.58839 0,0 -1.499942,-0.54491 -1.447105,-3.90494 z"
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path2904"
|
||||
sodipodi:nodetypes="ccccccccccc" /><path
|
||||
d="m 64.491103,152.99211 c 0.356304,1.02136 -0.206169,1.90598 -0.452664,2.81293 -0.285407,1.0506 0.531528,2.66525 -0.872464,3.18617 -3.056254,1.13409 -2.820371,-4.2538 -1.556164,-5.13196"
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:0.729413;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path2906"
|
||||
sodipodi:nodetypes="cccc" /><path
|
||||
d="m 60.574986,156.18338 c -0.429251,-0.21891 -0.583819,-0.69727 -0.547068,-1.18435"
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path2908" /><path
|
||||
d="m 67.2042,156.19033 c -0.717377,0.62372 -2.974059,1.02627 -3.230626,0.32462"
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path2910"
|
||||
sodipodi:nodetypes="cc" /><path
|
||||
d="m 67.436314,159.29719 c -1.317174,0.93586 -2.860074,0.81234 -3.832335,-0.34865"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.729413;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path2912" /><path
|
||||
style="fill:#ff6600;stroke:none;stroke-width:0.729413;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 61.642908,159.77009 v 0 l -0.196868,6.76242 c 0.138065,0.90621 3.047532,1.30724 2.913473,0.10362 -0.0029,-0.0261 0.07136,-3.35353 0.144362,-4.1272 -2.987756,-1.7577 -1.763448,-2.73153 -2.860967,-2.73884 z"
|
||||
id="path3853-6-4"
|
||||
sodipodi:nodetypes="cccscc" /><path
|
||||
d="m 62.40537,159.2052 c -1.058657,1.47852 2.221477,3.35076 2.098505,3.30373"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.729413;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path2914"
|
||||
sodipodi:nodetypes="cc" /><path
|
||||
d="m 61.475024,158.90397 c -0.304724,0.26545 -0.690797,0.6674 -1.175003,0.65813"
|
||||
style="fill:none;stroke:#000000;stroke-width:0.729413;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path2916" /><path
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 61.678487,158.97468 v 0 l -0.232447,7.55783 c 0.06153,1.02855 2.935348,1.19145 2.91348,0.10362 -6.49e-4,-0.0263 0.07135,-3.35353 0.144355,-4.1272"
|
||||
id="path3853-6"
|
||||
sodipodi:nodetypes="cccsc" /><path
|
||||
style="fill:#ff6600;stroke:none;stroke-width:0.729413;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 63.641927,124.75769 v 0 l -0.196869,-6.76242 c 0.138065,-0.90621 3.047532,-1.30724 2.913473,-0.10362 -0.0029,0.0261 0.07136,3.35353 0.144362,4.1272 -2.987755,1.7577 -1.763448,2.73153 -2.860966,2.73884 z"
|
||||
id="path3853-6-4-7"
|
||||
sodipodi:nodetypes="cccscc" /><path
|
||||
style="fill:none;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 63.677506,125.5531 v 0 l -0.232448,-7.55783 c 0.06153,-1.02855 2.935348,-1.19145 2.913481,-0.10362 -6.49e-4,0.0263 0.07135,3.35353 0.144355,4.1272"
|
||||
id="path3853-6-9"
|
||||
sodipodi:nodetypes="cccsc" /><path
|
||||
style="fill:#00b7b7;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="m 63.295152,131.49341 c 0.393707,0.51918 1.612281,0.99644 3.217121,0.7211 0,0 3.906619,1.4742 5.936024,1.38123 0.747349,-0.0342 1.382417,-0.72421 2.130338,-0.70639 2.811582,0.067 7.872764,3.03397 7.872764,3.03397 L 82.999,117.56681 c 0,0 -5.28177,-0.18252 -7.822554,0.42013 -0.958982,0.22745 -1.734177,1.04049 -2.709213,1.18432 -1.988881,0.29339 -5.988418,-0.7172 -5.988418,-0.7172 -0.919318,0.55366 -2.006773,0.51884 -3.183663,0.17099 z"
|
||||
id="path12766"
|
||||
sodipodi:nodetypes="ccssccssccc" /></g><path
|
||||
sodipodi:type="star"
|
||||
style="fill:#ffcc00;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path15414"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="117.51744"
|
||||
sodipodi:cy="29.413408"
|
||||
sodipodi:r1="11.625795"
|
||||
sodipodi:r2="5.8128977"
|
||||
sodipodi:arg1="-0.43776009"
|
||||
sodipodi:arg2="0.19055844"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m 128.04696,24.485096 -4.82184,6.029317 2.23323,7.390235 -7.22426,-2.722688 -6.33842,4.407635 0.35701,-7.712031 -6.15059,-4.666166 7.4449,-2.043609 2.53715,-7.291484 4.24419,6.44901 z"
|
||||
inkscape:transform-center-x="-0.15550371"
|
||||
inkscape:transform-center-y="-0.42661129"
|
||||
transform="matrix(0.5082118,0.10675533,-0.10675533,0.5082118,75.701411,28.86227)" /><path
|
||||
sodipodi:type="star"
|
||||
style="fill:#ffcc00;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path15414-2"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="117.51744"
|
||||
sodipodi:cy="29.413408"
|
||||
sodipodi:r1="11.625795"
|
||||
sodipodi:r2="5.8128977"
|
||||
sodipodi:arg1="-0.43776009"
|
||||
sodipodi:arg2="0.19055844"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m 128.04696,24.485096 -4.82184,6.029317 2.23323,7.390235 -7.22426,-2.722688 -6.33842,4.407635 0.35701,-7.712031 -6.15059,-4.666166 7.4449,-2.043609 2.53715,-7.291484 4.24419,6.44901 z"
|
||||
inkscape:transform-center-x="-0.11728461"
|
||||
inkscape:transform-center-y="-0.32177051"
|
||||
transform="matrix(0.38331637,0.0805197,-0.0805197,0.38331637,133.48215,38.188561)" /><path
|
||||
sodipodi:type="star"
|
||||
style="fill:#ffcc00;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path15414-2-2"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="117.51744"
|
||||
sodipodi:cy="29.413408"
|
||||
sodipodi:r1="11.625795"
|
||||
sodipodi:r2="5.8128977"
|
||||
sodipodi:arg1="-0.43776009"
|
||||
sodipodi:arg2="0.19055844"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m 128.04696,24.485096 -4.82184,6.029317 2.23323,7.390235 -7.22426,-2.722688 -6.33842,4.407635 0.35701,-7.712031 -6.15059,-4.666166 7.4449,-2.043609 2.53715,-7.291484 4.24419,6.44901 z"
|
||||
inkscape:transform-center-x="-0.054016501"
|
||||
inkscape:transform-center-y="-0.14818899"
|
||||
transform="matrix(0.17653378,0.03708281,-0.03708281,0.17653378,74.231021,67.773286)" /><path
|
||||
sodipodi:type="star"
|
||||
style="fill:#ffcc00;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path15414-2-6"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="117.51744"
|
||||
sodipodi:cy="29.413408"
|
||||
sodipodi:r1="11.625795"
|
||||
sodipodi:r2="5.8128977"
|
||||
sodipodi:arg1="-0.43776009"
|
||||
sodipodi:arg2="0.19055844"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m 128.04696,24.485096 -4.82184,6.029317 2.23323,7.390235 -7.22426,-2.722688 -6.33842,4.407635 0.35701,-7.712031 -6.15059,-4.666166 7.4449,-2.043609 2.53715,-7.291484 4.24419,6.44901 z"
|
||||
inkscape:transform-center-x="-0.10331594"
|
||||
inkscape:transform-center-y="-0.28344015"
|
||||
transform="matrix(0.33765476,0.070928,-0.070928,0.33765476,18.883161,63.313526)" /><path
|
||||
sodipodi:type="star"
|
||||
style="fill:#ffcc00;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path15414-0"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="117.51744"
|
||||
sodipodi:cy="29.413408"
|
||||
sodipodi:r1="11.625795"
|
||||
sodipodi:r2="5.8128977"
|
||||
sodipodi:arg1="-0.43776009"
|
||||
sodipodi:arg2="0.19055844"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m 128.04696,24.485096 -4.82184,6.029317 2.23323,7.390235 -7.22426,-2.722688 -6.33842,4.407635 0.35701,-7.712031 -6.15059,-4.666166 7.4449,-2.043609 2.53715,-7.291484 4.24419,6.44901 z"
|
||||
inkscape:transform-center-x="-0.15550652"
|
||||
inkscape:transform-center-y="-0.42661155"
|
||||
transform="matrix(0.5082118,0.10675533,-0.10675533,0.5082118,126.66807,99.717697)" /><path
|
||||
sodipodi:type="star"
|
||||
style="fill:#ffcc00;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path15416"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="156.13559"
|
||||
sodipodi:cy="46.936283"
|
||||
sodipodi:r1="5.2078781"
|
||||
sodipodi:r2="2.6039393"
|
||||
sodipodi:arg1="-0.43409146"
|
||||
sodipodi:arg2="0.19422707"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m 160.86045,44.745921 -2.16988,2.692944 0.98824,3.314168 -3.23167,-1.231515 -2.84658,1.964009 0.1726,-3.454063 -2.74753,-2.100344 3.33835,-0.903212 1.14851,-3.262094 1.89061,2.895847 z"
|
||||
inkscape:transform-center-x="-0.14002212"
|
||||
inkscape:transform-center-y="-0.36225272"
|
||||
transform="rotate(11.86311,-94.558638,67.518227)" /><path
|
||||
sodipodi:type="star"
|
||||
style="fill:#ffcc00;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path15418"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="65.107475"
|
||||
sodipodi:cy="28.732956"
|
||||
sodipodi:r1="11.61906"
|
||||
sodipodi:r2="5.8095307"
|
||||
sodipodi:arg1="2.7426294"
|
||||
sodipodi:arg2="3.3709479"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m 54.400928,33.246533 5.04915,-5.834372 -1.943775,-7.466965 7.109091,2.999107 6.500846,-4.156059 -0.65549,7.687923 5.961519,4.898379 -7.514207,1.75229 -2.816425,7.183424 -3.988544,-6.604947 z"
|
||||
inkscape:transform-center-x="0.43783231"
|
||||
inkscape:transform-center-y="0.68483807"
|
||||
transform="rotate(11.86311,-35.469528,138.62496)" /><path
|
||||
sodipodi:type="star"
|
||||
style="fill:#ffcc00;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path15420"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="29.340046"
|
||||
sodipodi:cy="46.625675"
|
||||
sodipodi:r1="4.2978187"
|
||||
sodipodi:r2="2.1489098"
|
||||
sodipodi:arg1="0.47181668"
|
||||
sodipodi:arg2="1.1001352"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m 33.168302,48.579056 -2.853778,-0.03813 -1.649258,2.32926 -0.845606,-2.725885 -2.724907,-0.848757 2.331164,-1.646565 -0.03483,-2.85382 2.286346,1.708253 2.703382,-0.915002 -0.918125,2.702323 z"
|
||||
inkscape:transform-center-x="0.34606094"
|
||||
inkscape:transform-center-y="-0.06710565"
|
||||
transform="rotate(11.86311,-35.469528,138.62496)" /><path
|
||||
sodipodi:type="star"
|
||||
style="fill:#ffcc00;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path15422"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="105.60214"
|
||||
sodipodi:cy="62.81638"
|
||||
sodipodi:r1="2.3678966"
|
||||
sodipodi:r2="1.1839486"
|
||||
sodipodi:arg1="0.48083515"
|
||||
sodipodi:arg2="1.1091537"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m 107.70154,63.911579 -1.57204,-0.03518 -0.9202,1.275066 -0.45233,-1.505976 -1.49702,-0.481145 1.29249,-0.89556 -0.005,-1.57243 1.25113,0.952488 1.49392,-0.49067 -0.51925,1.484231 z"
|
||||
inkscape:transform-center-x="0.18428571"
|
||||
inkscape:transform-center-y="-0.043561917"
|
||||
transform="rotate(11.86311,-35.469528,138.62496)" /><path
|
||||
sodipodi:type="star"
|
||||
style="fill:#ffcc00;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path15414-5"
|
||||
inkscape:flatsided="false"
|
||||
sodipodi:sides="5"
|
||||
sodipodi:cx="117.51744"
|
||||
sodipodi:cy="29.413408"
|
||||
sodipodi:r1="11.625795"
|
||||
sodipodi:r2="5.8128977"
|
||||
sodipodi:arg1="-0.43776009"
|
||||
sodipodi:arg2="0.19055844"
|
||||
inkscape:rounded="0"
|
||||
inkscape:randomized="0"
|
||||
d="m 128.04696,24.485096 -4.82184,6.029317 2.23323,7.390235 -7.22426,-2.722688 -6.33842,4.407635 0.35701,-7.712031 -6.15059,-4.666166 7.4449,-2.043609 2.53715,-7.291484 4.24419,6.44901 z"
|
||||
inkscape:transform-center-x="-0.094976792"
|
||||
inkscape:transform-center-y="-0.26055941"
|
||||
transform="matrix(0.31039809,0.06520244,-0.06520244,0.31039809,0.09441247,63.170233)" /><path
|
||||
style="fill:#ffffff;fill-opacity:0.147971;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 49.931372,110.52643 c -4.164658,0.57437 -11.397846,8.11038 -17.096209,12.16736 2.641961,4.65789 5.268944,5.61059 7.896367,6.67201 0.698573,-4.82863 3.070763,-10.68288 9.199842,-18.83937 z"
|
||||
id="path1154"
|
||||
sodipodi:nodetypes="cccc" /><path
|
||||
style="fill:#000000;fill-opacity:0.124582;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 74.850969,128.37443 1.67781,14.87299 c 0.555976,-0.0887 0.798905,-0.36021 1.00794,-0.65158 -0.555596,-5.28217 -1.141753,-10.59146 -0.941037,-15.20195 -0.386159,0.44667 -0.949792,0.78452 -1.744713,0.98054 z"
|
||||
id="path1156"
|
||||
sodipodi:nodetypes="ccccc" /><path
|
||||
style="fill:#ffffff;fill-opacity:0.355131;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 67.779789,128.47502 1.511355,13.66693 c -0.147003,0.28974 -0.72278,0.31653 -1.265416,0.36367 l -1.305133,-13.59823 c 0.606151,-0.0574 0.913932,-0.20785 1.061531,-0.40815"
|
||||
id="path1158"
|
||||
sodipodi:nodetypes="ccccc" /><path
|
||||
style="fill:#000000;fill-opacity:0.391885;stroke:none;stroke-width:0.264583px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
d="m 114.37266,178.32121 c 0,0 3.30143,3.56816 9.722,3.14179 6.42058,-0.42637 9.65822,-5.25905 11.32829,-9.07146 -7.10641,6.30325 -14.07144,7.76835 -21.05029,5.92967 z"
|
||||
id="path1160"
|
||||
sodipodi:nodetypes="czcc" /></g></svg>
|
After Width: | Height: | Size: 45 KiB |
Binary file not shown.
Before Width: | Height: | Size: 237 KiB |
@ -43,6 +43,10 @@ const update_datetimes = (event) => {
|
||||
|
||||
start_day.oldvalue = start_day.value;
|
||||
}
|
||||
else {
|
||||
new_date = new Date(start_day.value);
|
||||
end_day.value = formatDate(new_date);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (end_day.value && end_time.value && start_day.value) {
|
||||
|
673
src/agenda_culturel/static/location_field/js/form.js
Normal file
673
src/agenda_culturel/static/location_field/js/form.js
Normal file
@ -0,0 +1,673 @@
|
||||
var SequentialLoader = function() {
|
||||
var SL = {
|
||||
loadJS: function(src, onload) {
|
||||
//console.log(src);
|
||||
// add to pending list
|
||||
this._load_pending.push({'src': src, 'onload': onload});
|
||||
// check if not already loading
|
||||
if ( ! this._loading) {
|
||||
this._loading = true;
|
||||
// load first
|
||||
this.loadNextJS();
|
||||
}
|
||||
},
|
||||
|
||||
loadNextJS: function() {
|
||||
// get next
|
||||
var next = this._load_pending.shift();
|
||||
if (next == undefined) {
|
||||
// nothing to load
|
||||
this._loading = false;
|
||||
return;
|
||||
}
|
||||
// check not loaded
|
||||
if (this._load_cache[next.src] != undefined) {
|
||||
next.onload();
|
||||
this.loadNextJS();
|
||||
return; // already loaded
|
||||
}
|
||||
else {
|
||||
this._load_cache[next.src] = 1;
|
||||
}
|
||||
// load
|
||||
var el = document.createElement('script');
|
||||
el.type = 'application/javascript';
|
||||
el.src = next.src;
|
||||
// onload callback
|
||||
var self = this;
|
||||
el.onload = function(){
|
||||
//console.log('Loaded: ' + next.src);
|
||||
// trigger onload
|
||||
next.onload();
|
||||
// try to load next
|
||||
self.loadNextJS();
|
||||
};
|
||||
document.body.appendChild(el);
|
||||
},
|
||||
|
||||
_loading: false,
|
||||
_load_pending: [],
|
||||
_load_cache: {}
|
||||
};
|
||||
|
||||
return {
|
||||
loadJS: SL.loadJS.bind(SL)
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
!function($){
|
||||
var LocationFieldCache = {
|
||||
load: [],
|
||||
onload: {},
|
||||
|
||||
isLoading: false
|
||||
};
|
||||
|
||||
var LocationFieldResourceLoader;
|
||||
|
||||
$.locationField = function(options) {
|
||||
var LocationField = {
|
||||
options: $.extend({
|
||||
provider: 'google',
|
||||
providerOptions: {
|
||||
google: {
|
||||
api: '//maps.google.com/maps/api/js',
|
||||
mapType: 'ROADMAP'
|
||||
}
|
||||
},
|
||||
searchProvider: 'google',
|
||||
id: 'map',
|
||||
latLng: '0,0',
|
||||
mapOptions: {
|
||||
zoom: 9
|
||||
},
|
||||
basedFields: $(),
|
||||
inputField: $(),
|
||||
suffix: '',
|
||||
path: '',
|
||||
fixMarker: true
|
||||
}, options),
|
||||
|
||||
providers: /google|openstreetmap|mapbox/,
|
||||
searchProviders: /google|yandex|nominatim|addok/,
|
||||
|
||||
render: function() {
|
||||
this.$id = $('#' + this.options.id);
|
||||
|
||||
if ( ! this.providers.test(this.options.provider)) {
|
||||
this.error('render failed, invalid map provider: ' + this.options.provider);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! this.searchProviders.test(this.options.searchProvider)) {
|
||||
this.error('render failed, invalid search provider: ' + this.options.searchProvider);
|
||||
return;
|
||||
}
|
||||
|
||||
var self = this;
|
||||
|
||||
this.loadAll(function(){
|
||||
var mapOptions = self._getMapOptions(),
|
||||
map = self._getMap(mapOptions);
|
||||
|
||||
var marker = self._getMarker(map, mapOptions.center);
|
||||
|
||||
// fix issue w/ marker not appearing
|
||||
if (self.options.provider == 'google' && self.options.fixMarker)
|
||||
self.__fixMarker();
|
||||
|
||||
// watch based fields
|
||||
self._watchBasedFields(map, marker);
|
||||
});
|
||||
},
|
||||
|
||||
fill: function(latLng) {
|
||||
this.options.inputField.val(latLng.lat + ',' + latLng.lng);
|
||||
},
|
||||
|
||||
search: function(map, marker, address) {
|
||||
if (this.options.searchProvider === 'google') {
|
||||
var provider = new GeoSearch.GoogleProvider({ apiKey: this.options.providerOptions.google.apiKey });
|
||||
provider.search({query: address}).then(data => {
|
||||
if (data.length > 0) {
|
||||
var result = data[0],
|
||||
latLng = new L.LatLng(result.y, result.x);
|
||||
|
||||
marker.setLatLng(latLng);
|
||||
map.panTo(latLng);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
else if (this.options.searchProvider === 'yandex') {
|
||||
// https://yandex.com/dev/maps/geocoder/doc/desc/concepts/input_params.html
|
||||
var url = 'https://geocode-maps.yandex.ru/1.x/?format=json&geocode=' + address;
|
||||
|
||||
if (typeof this.options.providerOptions.yandex.apiKey !== 'undefined') {
|
||||
url += '&apikey=' + this.options.providerOptions.yandex.apiKey;
|
||||
}
|
||||
|
||||
var request = new XMLHttpRequest();
|
||||
request.open('GET', url, true);
|
||||
|
||||
request.onload = function () {
|
||||
if (request.status >= 200 && request.status < 400) {
|
||||
var data = JSON.parse(request.responseText);
|
||||
var pos = data.response.GeoObjectCollection.featureMember[0].GeoObject.Point.pos.split(' ');
|
||||
var latLng = new L.LatLng(pos[1], pos[0]);
|
||||
marker.setLatLng(latLng);
|
||||
map.panTo(latLng);
|
||||
} else {
|
||||
console.error('Yandex geocoder error response');
|
||||
}
|
||||
};
|
||||
|
||||
request.onerror = function () {
|
||||
console.error('Check connection to Yandex geocoder');
|
||||
};
|
||||
|
||||
request.send();
|
||||
}
|
||||
|
||||
else if (this.options.searchProvider === 'addok') {
|
||||
var url = 'https://api-adresse.data.gouv.fr/search/?limit=1&q=' + address;
|
||||
|
||||
var request = new XMLHttpRequest();
|
||||
request.open('GET', url, true);
|
||||
|
||||
request.onload = function () {
|
||||
if (request.status >= 200 && request.status < 400) {
|
||||
var data = JSON.parse(request.responseText);
|
||||
var pos = data.features[0].geometry.coordinates;
|
||||
var latLng = new L.LatLng(pos[1], pos[0]);
|
||||
marker.setLatLng(latLng);
|
||||
map.panTo(latLng);
|
||||
} else {
|
||||
console.error('Addok geocoder error response');
|
||||
}
|
||||
};
|
||||
|
||||
request.onerror = function () {
|
||||
console.error('Check connection to Addok geocoder');
|
||||
};
|
||||
|
||||
request.send();
|
||||
}
|
||||
|
||||
else if (this.options.searchProvider === 'nominatim') {
|
||||
var url = '//nominatim.openstreetmap.org/search?format=json&q=' + address;
|
||||
|
||||
var request = new XMLHttpRequest();
|
||||
request.open('GET', url, true);
|
||||
|
||||
request.onload = function () {
|
||||
if (request.status >= 200 && request.status < 400) {
|
||||
var data = JSON.parse(request.responseText);
|
||||
if (data.length > 0) {
|
||||
var pos = data[0];
|
||||
var latLng = new L.LatLng(pos.lat, pos.lon);
|
||||
marker.setLatLng(latLng);
|
||||
map.panTo(latLng);
|
||||
} else {
|
||||
console.error(address + ': not found via Nominatim');
|
||||
}
|
||||
} else {
|
||||
console.error('Nominatim geocoder error response');
|
||||
}
|
||||
};
|
||||
|
||||
request.onerror = function () {
|
||||
console.error('Check connection to Nominatim geocoder');
|
||||
};
|
||||
|
||||
request.send();
|
||||
}
|
||||
},
|
||||
|
||||
loadAll: function(onload) {
|
||||
this.$id.html('Loading...');
|
||||
|
||||
// resource loader
|
||||
if (LocationFieldResourceLoader == undefined)
|
||||
LocationFieldResourceLoader = SequentialLoader();
|
||||
|
||||
this.load.loader = LocationFieldResourceLoader;
|
||||
this.load.path = this.options.path;
|
||||
|
||||
var self = this;
|
||||
|
||||
this.load.common(function(){
|
||||
var mapProvider = self.options.provider,
|
||||
onLoadMapProvider = function() {
|
||||
var searchProvider = self.options.searchProvider + 'SearchProvider',
|
||||
onLoadSearchProvider = function() {
|
||||
self.$id.html('');
|
||||
onload();
|
||||
};
|
||||
|
||||
if (self.load[searchProvider] != undefined) {
|
||||
self.load[searchProvider](self.options.providerOptions[self.options.searchProvider] || {}, onLoadSearchProvider);
|
||||
}
|
||||
else {
|
||||
onLoadSearchProvider();
|
||||
}
|
||||
};
|
||||
|
||||
if (self.load[mapProvider] != undefined) {
|
||||
self.load[mapProvider](self.options.providerOptions[mapProvider] || {}, onLoadMapProvider);
|
||||
}
|
||||
else {
|
||||
onLoadMapProvider();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
load: {
|
||||
google: function(options, onload) {
|
||||
var js = [
|
||||
this.path + '/@googlemaps/js-api-loader/index.min.js',
|
||||
this.path + '/Leaflet.GoogleMutant.js',
|
||||
];
|
||||
|
||||
this._loadJSList(js, function(){
|
||||
const loader = new google.maps.plugins.loader.Loader({
|
||||
apiKey: options.apiKey,
|
||||
version: "weekly",
|
||||
});
|
||||
loader.load().then(() => onload());
|
||||
});
|
||||
},
|
||||
|
||||
googleSearchProvider: function(options, onload) {
|
||||
onload();
|
||||
//var url = options.api;
|
||||
|
||||
//if (typeof options.apiKey !== 'undefined') {
|
||||
// url += url.indexOf('?') === -1 ? '?' : '&';
|
||||
// url += 'key=' + options.apiKey;
|
||||
//}
|
||||
|
||||
//var js = [
|
||||
// url,
|
||||
// this.path + '/l.geosearch.provider.google.js'
|
||||
// ];
|
||||
|
||||
//this._loadJSList(js, function(){
|
||||
// // https://github.com/smeijer/L.GeoSearch/issues/57#issuecomment-148393974
|
||||
// L.GeoSearch.Provider.Google.Geocoder = new google.maps.Geocoder();
|
||||
|
||||
// onload();
|
||||
//});
|
||||
},
|
||||
|
||||
yandexSearchProvider: function (options, onload) {
|
||||
onload();
|
||||
},
|
||||
|
||||
mapbox: function(options, onload) {
|
||||
onload();
|
||||
},
|
||||
|
||||
openstreetmap: function(options, onload) {
|
||||
onload();
|
||||
},
|
||||
|
||||
common: function(onload) {
|
||||
var self = this,
|
||||
js = [
|
||||
// map providers
|
||||
this.path + '/leaflet/leaflet.js',
|
||||
// search providers
|
||||
this.path + '/leaflet-geosearch/geosearch.umd.js',
|
||||
],
|
||||
css = [
|
||||
// map providers
|
||||
this.path + '/leaflet/leaflet.css'
|
||||
];
|
||||
|
||||
// Leaflet docs note:
|
||||
// Include Leaflet JavaScript file *after* Leaflet’s CSS
|
||||
// https://leafletjs.com/examples/quick-start/
|
||||
this._loadCSSList(css, function(){
|
||||
self._loadJSList(js, onload);
|
||||
});
|
||||
},
|
||||
|
||||
_loadJS: function(src, onload) {
|
||||
this.loader.loadJS(src, onload);
|
||||
},
|
||||
|
||||
_loadJSList: function(srclist, onload) {
|
||||
this.__loadList(this._loadJS, srclist, onload);
|
||||
},
|
||||
|
||||
_loadCSS: function(src, onload) {
|
||||
if (LocationFieldCache.onload[src] != undefined) {
|
||||
onload();
|
||||
}
|
||||
else {
|
||||
LocationFieldCache.onload[src] = 1;
|
||||
onloadCSS(loadCSS(src), onload);
|
||||
}
|
||||
},
|
||||
|
||||
_loadCSSList: function(srclist, onload) {
|
||||
this.__loadList(this._loadCSS, srclist, onload);
|
||||
},
|
||||
|
||||
__loadList: function(fn, srclist, onload) {
|
||||
if (srclist.length > 1) {
|
||||
for (var i = 0; i < srclist.length-1; ++i) {
|
||||
fn.call(this, srclist[i], function(){});
|
||||
}
|
||||
}
|
||||
|
||||
fn.call(this, srclist[srclist.length-1], onload);
|
||||
}
|
||||
},
|
||||
|
||||
error: function(message) {
|
||||
console.log(message);
|
||||
this.$id.html(message);
|
||||
},
|
||||
|
||||
_getMap: function(mapOptions) {
|
||||
var map = new L.Map(this.options.id, mapOptions), layer;
|
||||
|
||||
if (this.options.provider == 'google') {
|
||||
layer = new L.gridLayer.googleMutant({
|
||||
type: this.options.providerOptions.google.mapType.toLowerCase(),
|
||||
});
|
||||
}
|
||||
else if (this.options.provider == 'openstreetmap') {
|
||||
layer = new L.tileLayer(
|
||||
'//{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||
maxZoom: 18
|
||||
});
|
||||
}
|
||||
else if (this.options.provider == 'mapbox') {
|
||||
layer = new L.tileLayer(
|
||||
'https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', {
|
||||
maxZoom: 18,
|
||||
accessToken: this.options.providerOptions.mapbox.access_token,
|
||||
id: 'mapbox/streets-v11'
|
||||
});
|
||||
}
|
||||
|
||||
map.addLayer(layer);
|
||||
|
||||
return map;
|
||||
},
|
||||
|
||||
_getMapOptions: function() {
|
||||
return $.extend(this.options.mapOptions, {
|
||||
center: this._getLatLng()
|
||||
});
|
||||
},
|
||||
|
||||
_getLatLng: function() {
|
||||
var l = this.options.latLng.split(',').map(parseFloat);
|
||||
return new L.LatLng(l[0], l[1]);
|
||||
},
|
||||
|
||||
_getMarker: function(map, center) {
|
||||
var self = this,
|
||||
markerOptions = {
|
||||
draggable: true
|
||||
};
|
||||
|
||||
var marker = L.marker(center, markerOptions).addTo(map);
|
||||
|
||||
marker.on('dragstart', function(){
|
||||
if (self.options.inputField.is('[readonly]'))
|
||||
marker.dragging.disable();
|
||||
else
|
||||
marker.dragging.enable();
|
||||
});
|
||||
|
||||
// fill input on dragend
|
||||
marker.on('dragend move', function(){
|
||||
if (!self.options.inputField.is('[readonly]'))
|
||||
self.fill(this.getLatLng());
|
||||
});
|
||||
|
||||
// place marker on map click
|
||||
map.on('click', function(e){
|
||||
if (!self.options.inputField.is('[readonly]')) {
|
||||
marker.setLatLng(e.latlng);
|
||||
marker.dragging.enable();
|
||||
}
|
||||
});
|
||||
|
||||
return marker;
|
||||
},
|
||||
|
||||
_watchBasedFields: function(map, marker) {
|
||||
var self = this,
|
||||
basedFields = this.options.basedFields,
|
||||
onchangeTimer,
|
||||
onchange = function() {
|
||||
if (!self.options.inputField.is('[readonly]')) {
|
||||
var values = basedFields.map(function() {
|
||||
var value = $(this).val();
|
||||
return value === '' ? null : value;
|
||||
});
|
||||
var address = values.toArray().join(', ');
|
||||
clearTimeout(onchangeTimer);
|
||||
onchangeTimer = setTimeout(function(){
|
||||
self.search(map, marker, address);
|
||||
}, 300);
|
||||
}
|
||||
};
|
||||
|
||||
basedFields.each(function(){
|
||||
var el = $(this);
|
||||
|
||||
if (el.is('select'))
|
||||
el.change(onchange);
|
||||
else
|
||||
el.keyup(onchange);
|
||||
});
|
||||
|
||||
if (this.options.inputField.val() === '') {
|
||||
var values = basedFields.map(function() {
|
||||
var value = $(this).val();
|
||||
return value === '' ? null : value;
|
||||
});
|
||||
var address = values.toArray().join(', ');
|
||||
if (address !== '')
|
||||
onchange();
|
||||
}
|
||||
},
|
||||
|
||||
__fixMarker: function() {
|
||||
$('.leaflet-map-pane').css('z-index', '2 !important');
|
||||
$('.leaflet-google-layer').css('z-index', '1 !important');
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
render: LocationField.render.bind(LocationField)
|
||||
}
|
||||
}
|
||||
|
||||
function dataLocationFieldObserver(callback) {
|
||||
function _findAndEnableDataLocationFields() {
|
||||
var dataLocationFields = $('input[data-location-field-options]');
|
||||
|
||||
dataLocationFields
|
||||
.filter(':not([data-location-field-observed])')
|
||||
.attr('data-location-field-observed', true)
|
||||
.each(callback);
|
||||
}
|
||||
|
||||
var observer = new MutationObserver(function(mutations){
|
||||
_findAndEnableDataLocationFields();
|
||||
});
|
||||
|
||||
var container = document.documentElement || document.body;
|
||||
|
||||
$(container).ready(function(){
|
||||
_findAndEnableDataLocationFields();
|
||||
});
|
||||
|
||||
observer.observe(container, {attributes: true});
|
||||
}
|
||||
|
||||
dataLocationFieldObserver(function(){
|
||||
var el = $(this);
|
||||
|
||||
var name = el.attr('name'),
|
||||
options = el.data('location-field-options'),
|
||||
basedFields = options.field_options.based_fields,
|
||||
pluginOptions = {
|
||||
id: 'map_' + name,
|
||||
inputField: el,
|
||||
latLng: el.val() || '0,0',
|
||||
suffix: options['search.suffix'],
|
||||
path: options['resources.root_path'],
|
||||
provider: options['map.provider'],
|
||||
searchProvider: options['search.provider'],
|
||||
providerOptions: {
|
||||
google: {
|
||||
api: options['provider.google.api'],
|
||||
apiKey: options['provider.google.api_key'],
|
||||
mapType: options['provider.google.map_type']
|
||||
},
|
||||
mapbox: {
|
||||
access_token: options['provider.mapbox.access_token']
|
||||
},
|
||||
yandex: {
|
||||
apiKey: options['provider.yandex.api_key']
|
||||
},
|
||||
},
|
||||
mapOptions: {
|
||||
zoom: options['map.zoom']
|
||||
}
|
||||
};
|
||||
|
||||
// prefix
|
||||
var prefixNumber;
|
||||
|
||||
try {
|
||||
prefixNumber = name.match(/-(\d+)-/)[1];
|
||||
} catch (e) {}
|
||||
|
||||
if (options.field_options.prefix) {
|
||||
var prefix = options.field_options.prefix;
|
||||
|
||||
if (prefixNumber != null) {
|
||||
prefix = prefix.replace(/__prefix__/, prefixNumber);
|
||||
}
|
||||
|
||||
basedFields = basedFields.map(function(n){
|
||||
return prefix + n
|
||||
});
|
||||
}
|
||||
|
||||
// based fields
|
||||
pluginOptions.basedFields = $(basedFields.map(function(n){
|
||||
return '#id_' + n
|
||||
}).join(','));
|
||||
|
||||
// render
|
||||
$.locationField(pluginOptions).render();
|
||||
});
|
||||
|
||||
}(jQuery || django.jQuery);
|
||||
|
||||
/*!
|
||||
loadCSS: load a CSS file asynchronously.
|
||||
[c]2015 @scottjehl, Filament Group, Inc.
|
||||
Licensed MIT
|
||||
*/
|
||||
(function(w){
|
||||
"use strict";
|
||||
/* exported loadCSS */
|
||||
var loadCSS = function( href, before, media ){
|
||||
// Arguments explained:
|
||||
// `href` [REQUIRED] is the URL for your CSS file.
|
||||
// `before` [OPTIONAL] is the element the script should use as a reference for injecting our stylesheet <link> before
|
||||
// By default, loadCSS attempts to inject the link after the last stylesheet or script in the DOM. However, you might desire a more specific location in your document.
|
||||
// `media` [OPTIONAL] is the media type or query of the stylesheet. By default it will be 'all'
|
||||
var doc = w.document;
|
||||
var ss = doc.createElement( "link" );
|
||||
var ref;
|
||||
if( before ){
|
||||
ref = before;
|
||||
}
|
||||
else {
|
||||
var refs = ( doc.body || doc.getElementsByTagName( "head" )[ 0 ] ).childNodes;
|
||||
ref = refs[ refs.length - 1];
|
||||
}
|
||||
|
||||
var sheets = doc.styleSheets;
|
||||
ss.rel = "stylesheet";
|
||||
ss.href = href;
|
||||
// temporarily set media to something inapplicable to ensure it'll fetch without blocking render
|
||||
ss.media = "only x";
|
||||
|
||||
// Inject link
|
||||
// Note: the ternary preserves the existing behavior of "before" argument, but we could choose to change the argument to "after" in a later release and standardize on ref.nextSibling for all refs
|
||||
// Note: `insertBefore` is used instead of `appendChild`, for safety re: http://www.paulirish.com/2011/surefire-dom-element-insertion/
|
||||
ref.parentNode.insertBefore( ss, ( before ? ref : ref.nextSibling ) );
|
||||
// A method (exposed on return object for external use) that mimics onload by polling until document.styleSheets until it includes the new sheet.
|
||||
var onloadcssdefined = function( cb ){
|
||||
var resolvedHref = ss.href;
|
||||
var i = sheets.length;
|
||||
while( i-- ){
|
||||
if( sheets[ i ].href === resolvedHref ){
|
||||
return cb();
|
||||
}
|
||||
}
|
||||
setTimeout(function() {
|
||||
onloadcssdefined( cb );
|
||||
});
|
||||
};
|
||||
|
||||
// once loaded, set link's media back to `all` so that the stylesheet applies once it loads
|
||||
ss.onloadcssdefined = onloadcssdefined;
|
||||
onloadcssdefined(function() {
|
||||
ss.media = media || "all";
|
||||
});
|
||||
return ss;
|
||||
};
|
||||
// commonjs
|
||||
if( typeof module !== "undefined" ){
|
||||
module.exports = loadCSS;
|
||||
}
|
||||
else {
|
||||
w.loadCSS = loadCSS;
|
||||
}
|
||||
}( typeof global !== "undefined" ? global : this ));
|
||||
|
||||
|
||||
/*!
|
||||
onloadCSS: adds onload support for asynchronous stylesheets loaded with loadCSS.
|
||||
[c]2014 @zachleat, Filament Group, Inc.
|
||||
Licensed MIT
|
||||
*/
|
||||
|
||||
/* global navigator */
|
||||
/* exported onloadCSS */
|
||||
function onloadCSS( ss, callback ) {
|
||||
ss.onload = function() {
|
||||
ss.onload = null;
|
||||
if( callback ) {
|
||||
callback.call( ss );
|
||||
}
|
||||
};
|
||||
|
||||
// This code is for browsers that don’t support onload, any browser that
|
||||
// supports onload should use that instead.
|
||||
// No support for onload:
|
||||
// * Android 4.3 (Samsung Galaxy S4, Browserstack)
|
||||
// * Android 4.2 Browser (Samsung Galaxy SIII Mini GT-I8200L)
|
||||
// * Android 2.3 (Pantech Burst P9070)
|
||||
|
||||
// Weak inference targets Android < 4.4
|
||||
if( "isApplicationInstalled" in navigator && "onloadcssdefined" in ss ) {
|
||||
ss.onloadcssdefined( callback );
|
||||
}
|
||||
}
|
@ -147,7 +147,7 @@ details[role="list"] summary + ul li.selected>a:hover {
|
||||
}
|
||||
|
||||
}
|
||||
.suggested-tags {
|
||||
.suggestions {
|
||||
font-size: 80%;
|
||||
}
|
||||
}
|
||||
@ -198,8 +198,18 @@ details[role="list"] summary + ul li.selected>a:hover {
|
||||
@extend .outline;
|
||||
font-size: 90%;
|
||||
padding: 0.15em 0.4em 0.3em 0.4em;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.suggestions .small-cat {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.small-location {
|
||||
@extend .small-cat;
|
||||
border-color: var(--contrast);
|
||||
color: var(--contrast);
|
||||
}
|
||||
|
||||
.circ-cat.circ-large {
|
||||
height: 2.6em;
|
||||
@ -267,6 +277,12 @@ svg {
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 600px) {
|
||||
.details-entete {
|
||||
padding-left: 28%;
|
||||
}
|
||||
}
|
||||
|
||||
.ephemeris-hour {
|
||||
@extend .ephemeris;
|
||||
padding: 1.5em 0.1em;
|
||||
@ -313,6 +329,7 @@ footer [data-tooltip] {
|
||||
scroll-behavior: smooth;
|
||||
transition-duration: 200ms;
|
||||
|
||||
|
||||
.cat {
|
||||
margin-right: 0;
|
||||
}
|
||||
@ -549,6 +566,12 @@ article#filters {
|
||||
font-size: 100%;
|
||||
}
|
||||
}
|
||||
.helptext {
|
||||
display: block;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
|
||||
header .remarque {
|
||||
font-style: italic;
|
||||
}
|
||||
@ -567,6 +590,8 @@ header .remarque {
|
||||
|
||||
.slide-buttons {
|
||||
float: right;
|
||||
max-width: 30em;
|
||||
text-align: right;
|
||||
}
|
||||
.left-buttons {
|
||||
display: inline-block;
|
||||
@ -863,6 +888,7 @@ nav>div {
|
||||
}
|
||||
|
||||
|
||||
|
||||
.badge.error {
|
||||
background: #b71c1c;
|
||||
border-color: #cb1b1b;
|
||||
@ -874,6 +900,25 @@ nav>div {
|
||||
color: var(--secondary-inverse);
|
||||
}
|
||||
|
||||
.tw-badge {
|
||||
background: black;
|
||||
border-color: black;
|
||||
color: #f2f207;
|
||||
font-weight: bold;
|
||||
padding: 0.2em 0.4em;
|
||||
font-size: 60%;
|
||||
opacity: 80%;
|
||||
}
|
||||
|
||||
.tw-prefix {
|
||||
background: black;
|
||||
color: #f2f207;
|
||||
height: 2.1em;
|
||||
margin: -0.5em 0 -0.5em -0.5em;
|
||||
padding: 0.3em;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
form [role="button"], form button {
|
||||
margin: var(--spacing) 0 var(--spacing) 0;
|
||||
}
|
||||
@ -886,6 +931,10 @@ form .buttons [role="button"] {
|
||||
margin: 2em auto;
|
||||
}
|
||||
|
||||
.min-y-grid {
|
||||
min-height: 4em;
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.grid.two-columns {
|
||||
grid-column-gap: var(--nav-element-spacing-vertical);
|
||||
@ -930,7 +979,7 @@ aside nav.paragraph li a, aside .no-breakline li a {
|
||||
}
|
||||
|
||||
/* mise en forme pour les récurrences */
|
||||
article form p .recurrence-widget {
|
||||
article form div .recurrence-widget {
|
||||
width: 100%;
|
||||
border: 0;
|
||||
|
||||
@ -1110,6 +1159,17 @@ table .buttons {
|
||||
}
|
||||
}
|
||||
|
||||
.image-flottante {
|
||||
width: 30%;
|
||||
float: right;
|
||||
margin: 0 1em;
|
||||
img {
|
||||
width: 100%;
|
||||
margin: -4em 0 0 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
img.preview {
|
||||
height: 100px;
|
||||
}
|
||||
@ -1252,15 +1312,31 @@ img.preview {
|
||||
}
|
||||
}
|
||||
.choices__list--dropdown {
|
||||
display: none;
|
||||
font-size: 80%;
|
||||
display: block;
|
||||
will-change: display;
|
||||
background: var(--background-color);
|
||||
padding: 0.4em;
|
||||
border-radius: 0.2em;
|
||||
box-shadow: 0 -1px 0 rgba(115, 130, 140, 0.2);
|
||||
position: relative;
|
||||
max-height: 300px;
|
||||
overflow: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
will-change: scroll-position;
|
||||
}
|
||||
.choices__list--dropdown.is-active {
|
||||
.choices__list--dropdown .choices__group,
|
||||
.choices__list--dropdown .choices__item {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.choices__list--dropdown .choices__item.visible,
|
||||
.choices__list--dropdown.is-active .choices__item {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.choices__list--dropdown .choices__group.visible,
|
||||
.choices__list--dropdown.is-active .choices__group {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
@ -1268,17 +1344,15 @@ img.preview {
|
||||
|
||||
|
||||
|
||||
article {
|
||||
.stick-bottom {
|
||||
background: var(--card-sectionning-background-color);
|
||||
margin-right: calc(var(--block-spacing-horizontal) * -1);
|
||||
margin-left: calc(var(--block-spacing-horizontal) * -1);
|
||||
padding: calc(var(--block-spacing-vertical) * 0.66) var(--block-spacing-horizontal);
|
||||
margin-bottom: calc(var(--block-spacing-vertical) * -1);
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
box-shadow: 0 -1px 0 rgba(115, 130, 140, 0.2);
|
||||
}
|
||||
.stick-bottom {
|
||||
background: var(--card-sectionning-background-color);
|
||||
margin-right: calc(var(--block-spacing-horizontal) * -1);
|
||||
margin-left: calc(var(--block-spacing-horizontal) * -1);
|
||||
padding: calc(var(--block-spacing-vertical) * 0.66) var(--block-spacing-horizontal);
|
||||
margin-bottom: calc(var(--block-spacing-vertical) * -1);
|
||||
position: sticky;
|
||||
bottom: 0;
|
||||
box-shadow: 0 -1px 0 rgba(115, 130, 140, 0.2);
|
||||
}
|
||||
|
||||
/*#menu-rechercher, #menu-ajouter, #menu-configurer {
|
||||
@ -1320,7 +1394,11 @@ article {
|
||||
|
||||
}
|
||||
|
||||
.a-venir, .place, .tag, .tag-descriptions {
|
||||
.a-venir article.single-event {
|
||||
scroll-margin-top: 7em;
|
||||
}
|
||||
|
||||
.a-venir, .place, .tag, .tag-descriptions, .organisation {
|
||||
article#filters {
|
||||
margin: 2em 0;
|
||||
}
|
||||
@ -1357,6 +1435,17 @@ article {
|
||||
}
|
||||
}
|
||||
|
||||
.header-complement {
|
||||
float: none;
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 992px) {
|
||||
.header-complement {
|
||||
float: left;
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
|
||||
form.messages div, form.moderation-events {
|
||||
@media only screen and (min-width: 992px) {
|
||||
display: grid;
|
||||
@ -1371,3 +1460,248 @@ form.messages div, form.moderation-events {
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
|
||||
.moderate-preview .event-body {
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
#moderate-form #id_status {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
}
|
||||
|
||||
label.required::after {
|
||||
content: ' *';
|
||||
color: red;
|
||||
}
|
||||
|
||||
.contenu #event_form {
|
||||
div#group_start, div#group_end {
|
||||
grid-column: auto;
|
||||
.body_group {
|
||||
display: grid;
|
||||
grid-gap: 10px;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* group form */
|
||||
@media only screen and (min-width: 992px) {
|
||||
.contenu #event_form form {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
grid-gap: 10px;
|
||||
|
||||
>div {
|
||||
grid-column: 1 / 3;
|
||||
}
|
||||
div#group_meta, div#group_meta-admin {
|
||||
.body_group {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
grid-gap: 10px;
|
||||
div {
|
||||
grid-column: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
div#group_meta {
|
||||
.body_group {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.maskable_group .body_group.closed {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.form-place {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(1, 1fr);
|
||||
row-gap: .5em;
|
||||
margin-bottom: 0.5em;
|
||||
.map-widget {
|
||||
grid-row: 3;
|
||||
}
|
||||
#group_address .body_group {
|
||||
display: grid;
|
||||
grid-template-columns: repear(2, 1fr);
|
||||
|
||||
column-gap: .5em;
|
||||
#div_id_address, #div_id_location {
|
||||
grid-column: 1 / 3;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@media only screen and (min-width: 992px) {
|
||||
.form-place {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
|
||||
.map-widget {
|
||||
grid-column: 2 / 3;
|
||||
grid-row: 1 / 3;
|
||||
}
|
||||
|
||||
#group_other {
|
||||
grid-column: 1 / 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.line-now {
|
||||
font-size: 60%;
|
||||
div {
|
||||
display: grid;
|
||||
grid-template-columns: fit-content(2em) auto;
|
||||
column-gap: .2em;
|
||||
color: red;
|
||||
.line {
|
||||
margin-top: .7em;
|
||||
border-top: 1px solid red;
|
||||
}
|
||||
}
|
||||
margin-bottom: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.a-venir .line-now {
|
||||
margin-left: -2em;
|
||||
|
||||
}
|
||||
|
||||
#chronology {
|
||||
.entree {
|
||||
display: grid;
|
||||
grid-template-columns: fit-content(2em) auto;
|
||||
column-gap: .7em;
|
||||
.texte {
|
||||
background: var(--background-color);
|
||||
padding: 0.1em 0.8em;
|
||||
border-radius: var(--border-radius);
|
||||
p {
|
||||
font-size: 100%;
|
||||
}
|
||||
p:last-child {
|
||||
margin-bottom: 0.1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
font-size: 85%;
|
||||
footer {
|
||||
margin-top: 1.8em;
|
||||
padding: 0.2em .8em;
|
||||
}
|
||||
.ts {
|
||||
@extend .badge-small;
|
||||
border-radius: var(--border-radius);
|
||||
display: inline-block;
|
||||
width: 14em;
|
||||
margin-right: 1.2em;
|
||||
}
|
||||
}
|
||||
|
||||
.moderation_heatmap {
|
||||
overflow-x: auto;
|
||||
table {
|
||||
max-width: 600px;
|
||||
margin: auto;
|
||||
.total, .month {
|
||||
display: none;
|
||||
}
|
||||
.label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
th {
|
||||
font-size: 90%;
|
||||
text-align: center;
|
||||
}
|
||||
td {
|
||||
font-size: 80%;
|
||||
text-align: center;
|
||||
}
|
||||
tbody th {
|
||||
text-align: right;
|
||||
}
|
||||
.ratio {
|
||||
padding: 0.1em;
|
||||
a, .a {
|
||||
margin: auto;
|
||||
border-radius: var(--border-radius);
|
||||
color: black;
|
||||
padding: 0;
|
||||
display: block;
|
||||
max-width: 6em;
|
||||
width: 3.2em;
|
||||
height: 2em;
|
||||
line-height: 2em;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
.score_0 {
|
||||
a, .a {
|
||||
background: rgb(0, 128, 0);
|
||||
}
|
||||
a:hover {
|
||||
background: rgb(0, 176, 0);
|
||||
}
|
||||
}
|
||||
.score_1 {
|
||||
a, .a {
|
||||
background: rgb(255, 255, 0);
|
||||
}
|
||||
a:hover {
|
||||
background: rgb(248, 248, 121);
|
||||
}
|
||||
}
|
||||
.score_2 {
|
||||
a, .a {
|
||||
background: rgb(255, 166, 0);
|
||||
}
|
||||
a:hover {
|
||||
background: rgb(255, 182, 47);
|
||||
}
|
||||
}
|
||||
.score_3 {
|
||||
a, .a {
|
||||
background: rgb(255, 0, 0);
|
||||
}
|
||||
a:hover {
|
||||
background: rgb(255, 91, 91);
|
||||
}
|
||||
}
|
||||
.score_4 {
|
||||
a, .a {
|
||||
background: rgb(128, 0, 128);
|
||||
color: white;
|
||||
}
|
||||
a:hover {
|
||||
background: rgb(178, 0, 178);
|
||||
}
|
||||
}
|
||||
@media only screen and (min-width: 1800px) {
|
||||
.total, .month {
|
||||
display: inline;
|
||||
opacity: .35;
|
||||
}
|
||||
.label {
|
||||
display: table-cell;
|
||||
}
|
||||
}
|
||||
@media only screen and (min-width: 1600px) {
|
||||
.ratio {
|
||||
a, .a {
|
||||
width: 5em;
|
||||
height: 3.2em;
|
||||
line-height: 3.2em;
|
||||
}
|
||||
}
|
||||
font-size: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,8 +17,52 @@
|
||||
{% block content %}
|
||||
<div class="grid two-columns">
|
||||
<div id="contenu-principal">
|
||||
<div>
|
||||
|
||||
<article>
|
||||
|
||||
<article>
|
||||
<header>
|
||||
<div class="slide-buttons">
|
||||
<a href="{% url 'moderate' %}" role="button">Modérer {% picto_from_name "check-square" %}</a>
|
||||
</div>
|
||||
<h2>Modération à venir</h2>
|
||||
</header>
|
||||
<div class="grid">
|
||||
<div>
|
||||
{% url 'administration' as local_url %}
|
||||
{% include "agenda_culturel/static_content.html" with name="administration" url_path=local_url %}
|
||||
</div>
|
||||
<div class="moderation_heatmap">
|
||||
{% for w in nb_not_moderated %}
|
||||
<table>
|
||||
<thead>
|
||||
<th class="label"></th>
|
||||
{% for m in w %}
|
||||
<th><a href="{% url 'day_view' m.start_day.year m.start_day.month m.start_day.day %}">{{ m.start_day|date:"d" }}<span class="month"> {{ m.start_day|date:"M"|lower }}</span></a></th>
|
||||
{% endfor %}
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th class="label">reste à modérer</h>
|
||||
{% for m in w %}
|
||||
<td class="ratio score_{{ m.note }}">
|
||||
<{% if m.not_moderated > 0 %}a href="{% if m.is_today %}
|
||||
{% url 'moderate' %}
|
||||
{% else %}
|
||||
{% url 'moderate_from_date' m.start_day.year m.start_day.month m.start_day.day %}
|
||||
{% endif %}"{% else %}span class="a"{% endif %}>
|
||||
{{ m.not_moderated }}<span class="total"> / {{ m.nb_events }}</span></{% if m.not_moderated > 0 %}a{% else %}span{% endif %}>
|
||||
</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
<article>
|
||||
<header>
|
||||
<h2>Activité des derniers jours</h2>
|
||||
</header>
|
||||
@ -32,11 +76,12 @@
|
||||
{% include "agenda_culturel/rimports-info-inc.html" with all=1 %}</p>
|
||||
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<article>
|
||||
<header>
|
||||
<div class="slide-buttons">
|
||||
<a href="{% url 'moderation' %}" role="button">Voir tout {% picto_from_name "chevron-right" %}</a>
|
||||
<a href="{% url 'recent' %}" role="button">Voir tout {% picto_from_name "chevron-right" %}</a>
|
||||
</div>
|
||||
<h2>Derniers événements soumis</h2>
|
||||
</header>
|
||||
|
@ -47,15 +47,21 @@
|
||||
<p>
|
||||
{% picto_from_name "calendar" %}
|
||||
{% if event.end_day and event.end_day != event.start_day %}du{% else %}le{% endif %}
|
||||
{% include "agenda_culturel/date-times-inc.html" with event=event %}
|
||||
</p>
|
||||
{{ event.start_day|date|frdate }}
|
||||
{% if event.start_time %} {% if not event.end_day or event.end_day == event.start_day %}{% if event.end_time %}de{% else %}à{% endif %}{% endif %}
|
||||
{{ event.start_time }}
|
||||
{% endif %}
|
||||
{% if event.end_day and event.end_day != event.start_day %}
|
||||
au {% if event.end_day and event.end_day != event.start_day %}{{ event.end_day|date|frdate }}{% endif %}
|
||||
{% endif %}
|
||||
{% if event.end_time %} {% if not event.end_day|date|frdate or event.end_day == event.start_day %}jusqu'à{% endif %} {{ event.end_time }}{% endif %} </p>
|
||||
</header>
|
||||
|
||||
<div class="body-fixed">{{ event.description |linebreaks }}</div>
|
||||
|
||||
|
||||
<p>
|
||||
{% for tag in event.tags %}
|
||||
{% for tag in event.sorted_tags %}
|
||||
{{ tag | tag_button }}
|
||||
{% endfor %}
|
||||
</p>
|
||||
|
@ -0,0 +1,27 @@
|
||||
{% extends "agenda_culturel/page-admin.html" %}
|
||||
|
||||
|
||||
{% block fluid %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<article>
|
||||
<header>
|
||||
<h1>{% block title %}{% block og_title %}Vider le cache{% endblock %}{% endblock %}</h1>
|
||||
</header>
|
||||
<form method="post">{% csrf_token %}
|
||||
<p>Êtes-vous sûr·e de vouloir vider le cache ? Toutes les pages seront
|
||||
générées lors de leur consultation, mais cela peut ralentir temporairemenet l'expérience de navigation.
|
||||
</p>
|
||||
{{ form }}
|
||||
|
||||
<footer>
|
||||
<div class="grid buttons">
|
||||
<a href="{{ cancel_url }}" role="button" class="secondary">Annuler</a>
|
||||
<input type="submit" value="Confirmer">
|
||||
</div>
|
||||
</footer>
|
||||
</form>
|
||||
</article>
|
||||
|
||||
{% endblock %}
|
@ -1,37 +0,0 @@
|
||||
{% extends "agenda_culturel/page-admin.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}{% block og_title %}Contact{% endblock %}{% endblock %}
|
||||
|
||||
{% block entete_header %}
|
||||
<script>window.CKEDITOR_BASEPATH = '/static/ckeditor/ckeditor/';</script>
|
||||
<script src="/static/admin/js/vendor/jquery/jquery.js"></script>
|
||||
<script src="/static/admin/js/jquery.init.js"></script>
|
||||
<link href="{% static 'css/django_better_admin_arrayfield.min.css' %}" type="text/css" media="all" rel="stylesheet">
|
||||
<script src="{% static 'js/django_better_admin_arrayfield.min.js' %}"></script>
|
||||
<script src="{% static 'js/adjust_datetimes.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block fluid %}{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h1>Contact</h1>
|
||||
|
||||
<article>
|
||||
{% url 'contact' as local_url %}
|
||||
{% include "agenda_culturel/static_content.html" with name="contact" url_path=local_url %}
|
||||
</article>
|
||||
|
||||
<article>
|
||||
<header>
|
||||
<p class="message warning"><strong>Attention :</strong> n'utilisez pas le formulaire ci-dessous pour proposer un événement, il sera ignoré. Utilisez plutôt la page <a href="{% url 'add_event' %}">ajouter un événement</a>.</p>
|
||||
</header>
|
||||
<form method="post">{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<input type="submit" value="Envoyer">
|
||||
</form>
|
||||
</article>
|
||||
|
||||
{% endblock %}
|
@ -1,10 +0,0 @@
|
||||
{% load utils_extra %}
|
||||
|
||||
{{ event.start_day|date|frdate }}
|
||||
{% if event.start_time %} {% if not event.end_day or event.end_day == event.start_day %}{% if event.end_time %}de{% else %}à{% endif %}{% endif %}
|
||||
{{ event.start_time }}
|
||||
{% endif %}
|
||||
{% if event.end_day and event.end_day != event.start_day %}
|
||||
au {% if event.end_day and event.end_day != event.start_day %}{{ event.end_day|date|frdate }}{% endif %}
|
||||
{% endif %}
|
||||
{% if event.end_time %} {% if not event.end_day|date|frdate or event.end_day == event.start_day %}jusqu'à{% endif %} {{ event.end_time }}{% endif %}
|
@ -1,82 +0,0 @@
|
||||
|
||||
{% load cat_extra %}
|
||||
{% load event_extra %}
|
||||
{% load utils_extra %}
|
||||
{% load static %}
|
||||
|
||||
{% with day.date|date:"Y-m-d" as daytag %}
|
||||
{% with "date-"|add:daytag as daytag %}
|
||||
|
||||
<article class="day{{ day|calendar_classes:fixed_style }}" id="{{ daytag }}">
|
||||
{% if day.is_today %}
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function(e) {
|
||||
agenda = document.getElementById("calendar");
|
||||
todayArticle = document.getElementById("today").parentElement;
|
||||
left = todayArticle.offsetLeft;
|
||||
top = todayArticle.offsetTop;
|
||||
agenda.scrollTo({top: top, left: left});
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
<header{% if day.is_today %} id="today"{% endif %}>
|
||||
<{% if headers != "" %}{{ headers }}{% else %}h2{% endif %}><a href="{{ day.date | url_day }}?{{ filter.get_url }}">{{ day.date | date:"l j" }}</a></{% if headers != "" %}{{ headers }}{% else %}h2{% endif %}>
|
||||
</header>
|
||||
{% if day.events %}
|
||||
{% if resume == 1 %}
|
||||
<ul>
|
||||
{% for category, events in day.events_by_category_ordered %}
|
||||
<li>{{ events.0.category | circle_cat }}
|
||||
<a href="{{ day.date | url_day }}?{{ filter.get_url|add_url_category:events.0.category }}" data-target="{{ daytag }}-category-{{ events.0.category.pk }}" onClick="toggleModal(event)">{{ events | length }} {{ category }}</a></li>
|
||||
<dialog id="{{ daytag }}-category-{{ events.0.category.pk }}">
|
||||
<article>
|
||||
<header>
|
||||
<a href="#{{ daytag }}-category-{{ events.0.category.pk }}"
|
||||
aria-label="Fermer"
|
||||
class="close"
|
||||
data-target="{{ daytag }}-category-{{ events.0.category.pk }}"
|
||||
onClick="toggleModal(event)"></a>
|
||||
<h3>{{ events.0.category | small_cat }} du {{ day.date | date:"l j F" }}</h3>
|
||||
</header>
|
||||
<ul>
|
||||
{% for event in events %}
|
||||
<li>
|
||||
{% if event.start_day == day.date and event.start_time %}
|
||||
{{ event.start_time }}
|
||||
{% endif %}
|
||||
<a href="{{ event.get_absolute_url }}">{{ event|picto_status }} {{ event.title }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<footer>
|
||||
<div class="buttons">
|
||||
<a href="{{ day.date | url_day }}?{{ filter.get_url }}" role="button">Voir la journée <svg width="1em" height="1em" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<use href="{% static 'images/feather-sprite.svg' %}#chevron-right" />
|
||||
</svg></a>
|
||||
</div>
|
||||
</footer>
|
||||
</article>
|
||||
</dialog>
|
||||
{% endfor %}
|
||||
<ul>
|
||||
{% else %}
|
||||
<ul>
|
||||
{% for event in day.events %}
|
||||
<li>{{ event.category | circle_cat:event.has_recurrences }}
|
||||
{% if event.start_day == day.date and event.start_time %}
|
||||
{{ event.start_time }}
|
||||
{% endif %}
|
||||
{{ event|picto_status }} <a href="{{ event.get_absolute_url }}" data-target="event-{{ event.id }}" onClick="toggleModal(event)">{{ event.title }}</a>
|
||||
<dialog id="event-{{ event.id }}">
|
||||
{% include "agenda_culturel/single-event/event-modal-inc.html" with event=event filter=filter %}
|
||||
</dialog>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</ul>
|
||||
</article>
|
||||
|
||||
{% endwith %}
|
||||
{% endwith %}
|
@ -1,13 +1,18 @@
|
||||
|
||||
{% load utils_extra %}
|
||||
|
||||
<!-- a href="{% url 'moderate_event' event.id %}" role="button">modérer {% picto_from_name "edit" %}</a-->
|
||||
{% if event.moderated_date %}
|
||||
<a href="{% url 'moderate_event' event.id %}" role="button">Modérer de nouveau {% picto_from_name "check-square" %}</a>
|
||||
{% else %}
|
||||
<a href="{% url 'moderate_event' event.id %}" role="button">Modérer {% picto_from_name "check-square" %}</a>
|
||||
{% endif %}
|
||||
|
||||
{% if event.pure_import %}
|
||||
{% with event.get_local_version as local %}
|
||||
{% if local %}
|
||||
<a href="{{ local.get_absolute_url }}" role="button">voir la version locale {% picto_from_name "eye" %}</a>
|
||||
{% else %}
|
||||
<a href="{% url 'clone_edit' event.id %}" role="button">créer une copie locale {% picto_from_name "plus-circle" %}</a>
|
||||
<a href="{% url 'clone_edit' event.id %}" role="button">modifier en copie locale {% picto_from_name "plus-circle" %}</a>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
{% else %}
|
||||
@ -36,3 +41,6 @@
|
||||
<a href="{% url 'delete_event' event.id %}" role="button">supprimer définitivement {% picto_from_name "x-circle" %}</a>
|
||||
{% endif %}
|
||||
|
||||
{% if with_clone %}
|
||||
<a href="{% url 'simple_clone_edit' event.id %}" role="button">Dupliquer {% picto_from_name "copy" %}</a>
|
||||
{% endif %}
|
||||
|
@ -1,13 +1,13 @@
|
||||
{% if user.is_authenticated %}
|
||||
<p class="footer">Création : {{ event.created_date }}
|
||||
<p class="footer">Création : {{ event.created_date }}{% if event.created_by_user %} par <em>{{ event.created_by_user.username }}</em>{% endif %}
|
||||
{% if event.modified %}
|
||||
— dernière modification : {{ event.modified_date }}
|
||||
— dernière modification : {{ event.modified_date }}{% if event.modified_by_user %} par <em>{{ event.modified_by_user.username }}</em>{% endif %}
|
||||
{% endif %}
|
||||
{% if event.imported_date %}
|
||||
— dernière importation : {{ event.imported_date }}
|
||||
— dernière importation : {{ event.imported_date }}{% if event.imported_by_user %} par <em>{{ event.imported_by_user.username }}</em>{% endif %}
|
||||
{% endif %}
|
||||
{% if event.moderated_date %}
|
||||
— dernière modération : {{ event.moderated_date }}
|
||||
— dernière modération : {{ event.moderated_date }}{% if event.moderated_by_user %} par <em>{{ event.moderated_by_user.username }}</em>{% endif %}
|
||||
{% endif %}
|
||||
{% if event.pure_import %}
|
||||
— <strong>version importée</strong>
|
||||
|
@ -1,10 +1,12 @@
|
||||
<footer class="remarque">
|
||||
Informations complémentaires non éditables :
|
||||
<strong>Informations complémentaires non éditables</strong>
|
||||
<ul>
|
||||
{% if object.created_date %}<li>Création : {{ object.created_date }}</li>{% endif %}
|
||||
{% if object.modified_date %}<li>Dernière modification : {{ object.modified_date }}</li>{% endif %}
|
||||
{% if object.moderated_date %}<li>Dernière modération : {{ object.moderated_date }}</li>{% endif %}
|
||||
{% if object.imported_date %}<li>Dernière importation : {{ object.imported_date }}</li>{% endif %}
|
||||
{% if not allbutdates %}
|
||||
{% if object.created_date %}<li>Création : {{ object.created_date }}{% if object.created_by_user %} par <em>{{ object.created_by_user.username }}</em>{% endif %}</li>{% endif %}
|
||||
{% if object.modified_date %}<li>Dernière modification : {{ object.modified_date }}{% if object.modified_by_user %} par <em>{{ object.modified_by_user.username }}</em>{% endif %}</li>{% endif %}
|
||||
{% if object.moderated_date %}<li>Dernière modération : {{ object.moderated_date }}{% if object.moderated_by_user %} par <em>{{ object.moderated_by_user.username }}</em>{% endif %}</li>{% endif %}
|
||||
{% if object.imported_date %}<li>Dernière importation : {{ object.imported_date }}{% if object.imported_by_user %} par <em>{{ object.imported_by_user.username }}</em>{% endif %}</li>{% endif %}
|
||||
{% endif %}
|
||||
{% if object.uuids %}
|
||||
{% if object.uuids|length > 0 %}
|
||||
<li>UUIDs (identifiants uniques d'événements dans les sources) :
|
||||
|
@ -1,14 +0,0 @@
|
||||
|
||||
{% if event.exact_location %}
|
||||
{% if nolink %}
|
||||
{{ event.exact_location.name }}, {{ event.exact_location.city }}
|
||||
{% else %}
|
||||
<a href="{{ event.exact_location.get_absolute_url }}">{{ event.exact_location.name }}, {{ event.exact_location.city }}</a>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% if perms.agenda_culturel.change_event and perms.agenda_culturel.change_place %}
|
||||
<a href="{% url 'add_place_to_event' event.pk %}" class="missing-data">{{ event.location }}</a>
|
||||
{% else %}
|
||||
{{ event.location }}
|
||||
{% endif %}
|
||||
{% endif %}
|
@ -14,16 +14,18 @@ Mettre l'événement {{ event.title }} à la corbeille
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h1>
|
||||
{% if status == "published" %}
|
||||
Publier l'événement {{ event.title }}
|
||||
{% elif status == "draft" %}
|
||||
Déplacer l'événement {{ event.title }} dans les brouillons
|
||||
{% elif status == "trash" %}
|
||||
Mettre l'événement {{ event.title }} à la corbeille
|
||||
{% endif %}
|
||||
</h1>
|
||||
<article>
|
||||
<header>
|
||||
<h1>
|
||||
{% if status == "published" %}
|
||||
Publier l'événement {{ event.title }}
|
||||
{% elif status == "draft" %}
|
||||
Déplacer l'événement {{ event.title }} dans les brouillons
|
||||
{% elif status == "trash" %}
|
||||
Mettre l'événement {{ event.title }} à la corbeille
|
||||
{% endif %}
|
||||
</h1>
|
||||
</header>
|
||||
<form method="post">{% csrf_token %}
|
||||
<p class="large">Êtes-vous sûr·e de vouloir
|
||||
{% if status == "published" %}
|
||||
|
@ -4,7 +4,11 @@
|
||||
|
||||
{% block title %}{% block og_title %}
|
||||
{% if object %}{% if form.is_clone_from_url %}
|
||||
Création d'une copie de {% else %}
|
||||
Création d'une copie locale de {% else %}
|
||||
Édition de l'événement {{ object.title }} ({{ object.start_day }})
|
||||
{% endif %}
|
||||
{% if form.is_simple_clone_from_url %}
|
||||
Duplication de {% else %}
|
||||
Édition de l'événement {{ object.title }} ({{ object.start_day }})
|
||||
{% endif %}
|
||||
{% else %}
|
||||
@ -44,13 +48,21 @@ Création d'une copie de {% else %}
|
||||
{% block content %}
|
||||
{% load static_content_extra %}
|
||||
|
||||
<article>
|
||||
<article id="event_form">
|
||||
<header>
|
||||
{% if object %}
|
||||
<h1>{% if form.is_clone_from_url %}
|
||||
Création d'une copie de {% else %}
|
||||
Édition de l'événement{% endif %} {{ object.title }} ({{ object.start_day }})</h1>
|
||||
Création d'une copie locale de {% else %}
|
||||
{% if form.is_simple_clone_from_url %}
|
||||
Duplication de {% else %}
|
||||
Édition de l'événement{% endif %}{% endif %} {{ object.title }} ({{ object.start_day }})</h1>
|
||||
{% if form.is_simple_clone_from_url %}
|
||||
<p>Vous allez créer une copie de l'événement original qui n'aura plus aucun lien avec ce dernier. Cette possibilité
|
||||
est généralement utilisée pour créer un événement semblable à une date différente, par exemple.
|
||||
</p>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
|
||||
{% if from_import %}
|
||||
<h1>Ajuster l'événement importé</h1>
|
||||
{% else %}
|
||||
@ -60,14 +72,14 @@ Création d'une copie de {% else %}
|
||||
{% endif %}
|
||||
</header>
|
||||
|
||||
<div id="container"></div>
|
||||
{% if form.is_clone_from_url %}
|
||||
|
||||
{% if form.is_clone_from_url or form.is_simple_clone_from_url %}
|
||||
{% url "add_event_details" as urlparam %}
|
||||
{% endif %}
|
||||
<form method="post" action="{{ urlparam }}" enctype="multipart/form-data">{% csrf_token %}
|
||||
{{ form.media }}
|
||||
{{ form.as_p }}
|
||||
<div class="grid buttons">
|
||||
{{ form }}
|
||||
<div class="grid buttons stick-bottom">
|
||||
<a href="{% if request.META.HTTP_REFERER %}{{ request.META.HTTP_REFERER }}{% else %}{{ object.get_absolute_url }}{% endif %}" role="button" class="secondary">Annuler</a>
|
||||
<input type="submit" value="Enregistrer">
|
||||
</div>
|
||||
@ -82,5 +94,44 @@ Création d'une copie de {% else %}
|
||||
const element = document.querySelector('#id_exact_location');
|
||||
const choices = new Choices(element);
|
||||
|
||||
show_firstgroup = {
|
||||
choice(classes, choice) {
|
||||
const i = Choices.defaults.templates.choice.call(this, classes, choice);
|
||||
if (this.first_group !== null && choice.groupId == this.first_group)
|
||||
i.classList.add("visible");
|
||||
return i;
|
||||
},
|
||||
choiceGroup(classes, group) {
|
||||
const g = Choices.defaults.templates.choiceGroup.call(this, classes, group);
|
||||
if (this.first_group === undefined && group.value == "Suggestions")
|
||||
this.first_group = group.id;
|
||||
if (this.first_group !== null && group.id == this.first_group)
|
||||
g.classList.add("visible");
|
||||
return g;
|
||||
}
|
||||
};
|
||||
const tags = document.querySelector('#id_tags');
|
||||
const choices_tags = new Choices(tags,
|
||||
{
|
||||
placeholderValue: 'Sélectionner les étiquettes à ajouter',
|
||||
allowHTML: true,
|
||||
delimiter: ',',
|
||||
removeItemButton: true,
|
||||
shouldSort: false,
|
||||
callbackOnCreateTemplates: () => (show_firstgroup)
|
||||
}
|
||||
);
|
||||
|
||||
const organisers = document.querySelector('#id_organisers');
|
||||
const choices_organisers = new Choices(organisers,
|
||||
{
|
||||
placeholderValue: 'Sélectionner les organisateurs ',
|
||||
allowHTML: true,
|
||||
delimiter: ',',
|
||||
removeItemButton: true,
|
||||
shouldSort: true,
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
@ -0,0 +1,131 @@
|
||||
{% extends "agenda_culturel/page.html" %}
|
||||
{% load static %}
|
||||
{% load utils_extra %}
|
||||
{% load cat_extra %}
|
||||
|
||||
|
||||
{% block title %}{% block og_title %}Modération de l'événement {{ object.title }}{% endblock %}{% endblock %}
|
||||
|
||||
{% block ajouter-bouton %}{% block ajouter-menu %}{% endblock %}{% endblock %}
|
||||
|
||||
|
||||
{% block entete_header %}
|
||||
<script src="{% static 'choicejs/choices.min.js' %}"></script>
|
||||
{% css_categories %}
|
||||
|
||||
<script src="/static/admin/js/vendor/jquery/jquery.js"></script>
|
||||
<script src="/static/admin/js/jquery.init.js"></script>
|
||||
|
||||
<link href="{% static 'css/django_better_admin_arrayfield.min.css' %}" type="text/css" media="all" rel="stylesheet">
|
||||
<script src="{% static 'js/django_better_admin_arrayfield.min.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
{% load static_content_extra %}
|
||||
|
||||
<article>
|
||||
<header>
|
||||
<h1>Modération de l'événement {{ object.title }}</h1>
|
||||
<p class="remarque">En utilisant cet outil de modération, l'événement ne sera pas modifié{% if event.pure_import %}
|
||||
et restera considéré comme une version importée, peut importe les modifications d'étiquette,
|
||||
de catégorie ou de lieu que vous lui appliquerez{% endif %}.
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<form method="post" enctype="multipart/form-data" id="moderate-form">{% csrf_token %}
|
||||
|
||||
<div class="grid moderate-preview">
|
||||
|
||||
<div>
|
||||
{% include "agenda_culturel/single-event/event-single-inc.html" with event=event onlyedit=1 %}
|
||||
|
||||
{% with event.get_concurrent_events as concurrent_events %}
|
||||
{% if concurrent_events %}
|
||||
<article>
|
||||
<header>
|
||||
<h2>En même temps</h2>
|
||||
<p class="remarque">{% if concurrent_events|length > 1 %}Plusieurs événements se déroulent en même temps.{% else %}Un autre événement se déroule en même temps.{% endif %}</p>
|
||||
</header>
|
||||
<ul>
|
||||
{% for e in concurrent_events %}
|
||||
<li>
|
||||
{{ e.category|circle_cat }} {% if e.start_time %}{{ e.start_time }}{% else %}<em>toute la journée</em>{% endif %} <a href="{{ e.get_absolute_url }}">{{ e.title }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</article>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
</div>
|
||||
|
||||
<article>
|
||||
<header>
|
||||
|
||||
<h2>Modification des méta-informations</h2>
|
||||
{% if event.moderated_date %}
|
||||
<p class="message info">Cet événement a déjà été modéré {% if event.moderation_by_user %}par {<em>{ event.moderation_by_user.username }}</em> {% endif %}le {{ event.moderated_date }}.
|
||||
Vous pouvez bien sûr modifier de nouveau ces méta-informations en utilisant
|
||||
le formulaire ci-après.
|
||||
</p>
|
||||
{% endif %}
|
||||
</header>
|
||||
{{ form.media }}
|
||||
{{ form.as_p }}
|
||||
</article>
|
||||
</div>
|
||||
<div class="grid buttons">
|
||||
{% if pred %}
|
||||
<a href="{% url 'moderate_event' pred %}" class="secondary" role="button">< Revenir au précédent</a>
|
||||
{% else %}
|
||||
<a href="{% if request.META.HTTP_REFERER %}{{ request.META.HTTP_REFERER }}{% else %}{{ object.get_absolute_url }}{% endif %}" role="button" class="secondary">Annuler</a>
|
||||
{% endif %}
|
||||
<input type="submit" value="Enregistrer" name="save">
|
||||
<input type="submit" value="Enregistrer et passer au suivant >" name="save_and_next">
|
||||
<a href="{% url 'moderate_event_next' event.pk %}" class="secondary" role="button">Passer au suivant sans enregistrer ></a>
|
||||
</div>
|
||||
</form>
|
||||
</article>
|
||||
|
||||
<script>
|
||||
const element = document.querySelector('#id_exact_location');
|
||||
const choices = new Choices(element);
|
||||
|
||||
show_firstgroup = {
|
||||
choice(classes, choice) {
|
||||
const i = Choices.defaults.templates.choice.call(this, classes, choice);
|
||||
i.classList.add("visible");
|
||||
return i;
|
||||
},
|
||||
choiceGroup(classes, group) {
|
||||
const g = Choices.defaults.templates.choiceGroup.call(this, classes, group);
|
||||
g.classList.add("visible");
|
||||
return g;
|
||||
}
|
||||
};
|
||||
const tags = document.querySelector('#id_tags');
|
||||
const choices_tags = new Choices(tags,
|
||||
{
|
||||
placeholderValue: 'Sélectionner les étiquettes à ajouter',
|
||||
allowHTML: true,
|
||||
delimiter: ',',
|
||||
removeItemButton: true,
|
||||
shouldSort: false,
|
||||
callbackOnCreateTemplates: () => (show_firstgroup)
|
||||
}
|
||||
);
|
||||
const organisers = document.querySelector('#id_organisers');
|
||||
const choices_organisers = new Choices(organisers,
|
||||
{
|
||||
placeholderValue: 'Sélectionner les organisateurs ',
|
||||
allowHTML: true,
|
||||
delimiter: ',',
|
||||
removeItemButton: true,
|
||||
shouldSort: true,
|
||||
});
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
@ -1,39 +0,0 @@
|
||||
{% extends "agenda_culturel/event_form.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% load cat_extra %}
|
||||
{% block entete_header %}
|
||||
{% css_categories %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block title %}{% block og_title %}Modérer {{ object.title }}{% endblock %}{% endblock %}
|
||||
|
||||
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% load static_content_extra %}
|
||||
|
||||
<article>
|
||||
<header>
|
||||
<h1>Modération de l'événement {{ object.title }} ({{ object.start_day }})</h1>
|
||||
</header>
|
||||
|
||||
{% include "agenda_culturel/single-event/event-single-inc.html" with event=object noedit=1 %}
|
||||
|
||||
<div id="container"></div>
|
||||
<form method="post">{% csrf_token %}
|
||||
{{ form.media }}
|
||||
{{ form.as_p }}
|
||||
<div class="grid buttons">
|
||||
<a href="{% if request.META.HTTP_REFERER %}{{ request.META.HTTP_REFERER }}{% else %}{{ object.get_absolute_url }}{% endif %}" role="button" class="secondary">Annuler</a>
|
||||
<input type="submit" value="Enregistrer">
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% include "agenda_culturel/event-info-inc.html" %}
|
||||
|
||||
</article>
|
||||
|
||||
{% endblock %}
|
@ -0,0 +1,40 @@
|
||||
{% extends "agenda_culturel/page.html" %}
|
||||
|
||||
{% load utils_extra %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}{% block og_title %}Erreur pendant la recherche d'événement suivant{% endblock %}{% endblock %}
|
||||
|
||||
|
||||
{% block entete_header %}
|
||||
<script src="{% static 'js/modal.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block fluid %}{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
|
||||
<article>
|
||||
<header>
|
||||
<h1>Erreur pendant la recherche d'événement suivant</h1>
|
||||
</header>
|
||||
{% if object %}
|
||||
<p>Nous n'avons pas été capable de trouver un événement à modérer après
|
||||
l'événement <a href="{{ object.get_absolute_url }}">{{ object.title }}</a>. Peut-être est-ce le dernier événement
|
||||
avant la fin du monde ?
|
||||
</p>
|
||||
{% else %}
|
||||
<p>Nous n'avons pas été capable de trouver l'événement #{{ pk }} sur l'agenda.
|
||||
Il a certainement été supprimé depuis que vous l'avez consulté.
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<footer>
|
||||
<div class="grid buttons">
|
||||
<a href="{% url 'recent' %}" role="button">Retour aux événements récents</a>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</article>
|
||||
{% endblock %}
|
@ -2,6 +2,7 @@
|
||||
{% load tag_extra %}
|
||||
{% load utils_extra %}
|
||||
{% load static %}
|
||||
{% load locations_extra %}
|
||||
|
||||
{% if noarticle == 0 %}
|
||||
<article id="filters">
|
||||
@ -13,6 +14,7 @@
|
||||
|
||||
<div class="filtres">
|
||||
<details {% if filter.is_active %}class="active"{% endif %} {% if filter.form and filter.form.errors %}open=""{% endif %}>
|
||||
{{ filter.form.non_field_errors }}
|
||||
<summary role="button" class="outline secondary">
|
||||
{% if filter.is_active %}
|
||||
<strong>Filtres : </strong>
|
||||
@ -20,7 +22,7 @@
|
||||
{{ t | tag_button:"true" }}
|
||||
{% endfor %}
|
||||
{% for t in filter.get_exclude_tags %}
|
||||
{{ t | tag_button_strike }}
|
||||
{{ t | tag_button_strike:"true" }}
|
||||
{% endfor %}
|
||||
{% for s in filter.get_status_names %}
|
||||
{{ s }}
|
||||
@ -39,7 +41,6 @@
|
||||
<form method="get" class="form django-form main-filter">
|
||||
<div class="grid two-columns-equal">
|
||||
<article>
|
||||
<header>Localisation</header>
|
||||
{% for f in filter.form.visible_fields %}
|
||||
{% if f.id_for_label and f.id_for_label in "id_position,id_radius" %}
|
||||
<div>
|
||||
@ -51,7 +52,6 @@
|
||||
{% endfor %}
|
||||
</article>
|
||||
<article>
|
||||
<header>Autres filtres</header>
|
||||
{% for f in filter.form.visible_fields %}
|
||||
{% if not f.id_for_label or not f.id_for_label in "id_position,id_radius" %}
|
||||
<p>
|
||||
@ -66,25 +66,50 @@
|
||||
<button type="submit">Appliquer le filtre</button>
|
||||
</form>
|
||||
</details>
|
||||
<div class="suggested-tags">
|
||||
{% show_suggested_tags filter=filter %}
|
||||
<div class="suggestions">
|
||||
Suggestion :
|
||||
{% show_suggested_positions filter=filter %}
|
||||
{% show_suggested_tags filter=filter %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
<script src="{% static 'choicejs/choices.min.js' %}"></script>
|
||||
<script>
|
||||
show_firstgroup = {
|
||||
choice(classes, choice) {
|
||||
const i = Choices.defaults.templates.choice.call(this, classes, choice);
|
||||
if (this.first_group !== null && choice.groupId == this.first_group)
|
||||
i.classList.add("visible");
|
||||
return i;
|
||||
},
|
||||
choiceGroup(classes, group) {
|
||||
const g = Choices.defaults.templates.choiceGroup.call(this, classes, group);
|
||||
if (this.first_group === undefined && group.value == "Suggestions")
|
||||
this.first_group = group.id;
|
||||
if (this.first_group !== null && group.id == this.first_group)
|
||||
g.classList.add("visible");
|
||||
return g;
|
||||
}
|
||||
};
|
||||
|
||||
const position = document.querySelector('#id_position');
|
||||
const choices_position = new Choices(position);
|
||||
const choices_position = new Choices(position,
|
||||
{
|
||||
shouldSort: false,
|
||||
|
||||
}
|
||||
);
|
||||
const tags = document.querySelector('#id_tags');
|
||||
const choices_tags = new Choices(tags,
|
||||
{
|
||||
placeholderValue: 'Chercher les étiquettes à inclure',
|
||||
allowHTML: true,
|
||||
delimiter: ',',
|
||||
editItems: true,
|
||||
removeItemButton: true,
|
||||
}
|
||||
shouldSort: false,
|
||||
callbackOnCreateTemplates: () => (show_firstgroup)
|
||||
}
|
||||
);
|
||||
const exclude_tags = document.querySelector('#id_exclude_tags');
|
||||
const choices_exclude_tags = new Choices(exclude_tags,
|
||||
@ -92,15 +117,12 @@
|
||||
placeholderValue: 'Chercher les étiquettes à exclure',
|
||||
allowHTML: true,
|
||||
delimiter: ',',
|
||||
editItems: true,
|
||||
removeItemButton: true,
|
||||
shouldSort: false,
|
||||
callbackOnCreateTemplates: () => (show_firstgroup)
|
||||
}
|
||||
);
|
||||
|
||||
document.querySelector("#id_position .choices__inner").classList.add("contrast");
|
||||
document.querySelector("#id_position .choices__inner").classList.add("outline");
|
||||
document.querySelector("#id_position .choices__inner").setAttribute("role", "button");
|
||||
|
||||
</script>
|
||||
|
||||
{% if noarticle == 0 %}
|
||||
|
@ -0,0 +1,67 @@
|
||||
|
||||
{{ errors }}
|
||||
{% if errors and not fields %}
|
||||
<p>{% for field in hidden_fields %}{{ field }}{% endfor %}</p>
|
||||
{% endif %}
|
||||
{% for group, fields in form.fields_by_group %}
|
||||
<div {% if group.maskable %}class="maskable_group"{% endif %} id="group_{{ group.id }}">
|
||||
{% if group.maskable %}
|
||||
<input
|
||||
class="toggle_body"
|
||||
type="checkbox"
|
||||
id="maskable_group_{{ group.id }}"
|
||||
name="group_{{ group.id }}"
|
||||
{% if not group.default_masked %}checked{% endif %}
|
||||
><label for="maskable_group_{{ group.id }}">{{ group.label }}</label>
|
||||
{% endif %}
|
||||
<div class="error_group">
|
||||
{% for field in fields %}
|
||||
<div id="error_{{ field.auto_id }}">
|
||||
{{ field.errors }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="body_group">
|
||||
{% for field in fields %}
|
||||
<div id="div_{{ field.auto_id }}">
|
||||
<div{% with classes=field.css_classes %}{% if classes %} class="{{ classes }}"{% endif %}{% endwith %}>
|
||||
{% if field.label %}{{ field.label_tag }}{% endif %}
|
||||
{{ field }}
|
||||
{% if field.help_text %}
|
||||
<span class="helptext"{% if field.auto_id %} id="{{ field.auto_id }}_helptext"{% endif %}>{{ field.help_text|safe }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% if not errors %}
|
||||
{% for field in hidden_fields %}{{ field }}{% endfor %}
|
||||
{% endif %}
|
||||
<script>
|
||||
const maskables = document.querySelectorAll('.maskable_group');
|
||||
maskables.forEach(function (item) {
|
||||
if (!item.checked) {
|
||||
item.querySelector('.body_group').classList.add('closed');
|
||||
}
|
||||
console.log('item ' + item);
|
||||
|
||||
item.querySelector('.toggle_body').addEventListener('change', (event) => {
|
||||
if (event.currentTarget.checked) {
|
||||
item.querySelector('.body_group').classList.remove('closed');
|
||||
} else {
|
||||
item.querySelector('.body_group').classList.add('closed');
|
||||
}
|
||||
})
|
||||
|
||||
window.addEventListener('load', function(event) {
|
||||
if (item.querySelector('.toggle_body').checked) {
|
||||
item.querySelector('.body_group').classList.remove('closed');
|
||||
}
|
||||
else {
|
||||
item.querySelector('.body_group').classList.add('closed');
|
||||
}
|
||||
});
|
||||
})
|
||||
</script>
|
@ -31,4 +31,35 @@
|
||||
tout de même ajouter l'événement en le décrivant sur la page <a href="{% url 'add_event_details' %}" >d'ajout d'événement</a>.</p>
|
||||
</article>
|
||||
|
||||
<script src="{% static 'choicejs/choices.min.js' %}"></script>
|
||||
<script>
|
||||
show_firstgroup = {
|
||||
choice(classes, choice) {
|
||||
const i = Choices.defaults.templates.choice.call(this, classes, choice);
|
||||
if (this.first_group !== null && choice.groupId == this.first_group)
|
||||
i.classList.add("visible");
|
||||
return i;
|
||||
},
|
||||
choiceGroup(classes, group) {
|
||||
const g = Choices.defaults.templates.choiceGroup.call(this, classes, group);
|
||||
if (this.first_group === undefined && group.value == "Suggestions")
|
||||
this.first_group = group.id;
|
||||
if (this.first_group !== null && group.id == this.first_group)
|
||||
g.classList.add("visible");
|
||||
return g;
|
||||
}
|
||||
};
|
||||
const tags = document.querySelector('#id_tags');
|
||||
const choices_tags = new Choices(tags,
|
||||
{
|
||||
placeholderValue: 'Sélectionner les étiquettes à ajouter',
|
||||
allowHTML: true,
|
||||
delimiter: ',',
|
||||
removeItemButton: true,
|
||||
shouldSort: false,
|
||||
callbackOnCreateTemplates: () => (show_firstgroup)
|
||||
}
|
||||
);
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
@ -18,27 +18,62 @@
|
||||
{% url 'event_import_urls' as local_url %}
|
||||
{% include "agenda_culturel/static_content.html" with name="import_set" url_path=local_url %}
|
||||
</header>
|
||||
|
||||
<p>Si l'import automatique ne marche pas, ou si l'événement n'est pas en ligne, vous pouvez
|
||||
tout de même ajouter l'événement en le décrivant sur la page <a href="{% url 'add_event_details' %}" >d'ajout d'événement</a>.</p>
|
||||
</article>
|
||||
|
||||
|
||||
<form method="post">
|
||||
|
||||
{{ formset.management_form }}
|
||||
{% csrf_token %}
|
||||
{% for form in formset %}
|
||||
<h2>Nouvel événement #{{ forloop.counter }}</h2>
|
||||
<article>
|
||||
<header>
|
||||
<h2>Nouvel événement #{{ forloop.counter }}</h2>
|
||||
</header>
|
||||
{{ form.as_p }}
|
||||
</article>
|
||||
{% endfor %}
|
||||
|
||||
<div class="stick-bottom">
|
||||
<input type="submit"value="Lancer l'import" id="import-button">
|
||||
</div>
|
||||
</form>
|
||||
<footer>
|
||||
<p>Si l'import automatique ne marche pas, ou si l'événement n'est pas en ligne, vous pouvez
|
||||
tout de même ajouter l'événement en le décrivant sur la page <a href="{% url 'add_event_details' %}" >d'ajout d'événement</a>.</p>
|
||||
</footer>
|
||||
</article>
|
||||
<dialog id="modal-import">
|
||||
<article aria-busy="true">
|
||||
Veuillez patienter, lien en cours d'importation...
|
||||
</article>
|
||||
</dialog>
|
||||
|
||||
<script src="{% static 'choicejs/choices.min.js' %}"></script>
|
||||
<script>
|
||||
show_firstgroup = {
|
||||
choice(classes, choice) {
|
||||
const i = Choices.defaults.templates.choice.call(this, classes, choice);
|
||||
if (this.first_group !== null && choice.groupId == this.first_group)
|
||||
i.classList.add("visible");
|
||||
return i;
|
||||
},
|
||||
choiceGroup(classes, group) {
|
||||
const g = Choices.defaults.templates.choiceGroup.call(this, classes, group);
|
||||
if (this.first_group === undefined && group.value == "Suggestions")
|
||||
this.first_group = group.id;
|
||||
if (this.first_group !== null && group.id == this.first_group)
|
||||
g.classList.add("visible");
|
||||
return g;
|
||||
}
|
||||
};
|
||||
const tags = document.querySelectorAll("select[name$='-tags']");
|
||||
Array.from(tags).forEach((element,index) => {
|
||||
console.log(element);
|
||||
const choices_tags = new Choices(element,
|
||||
{
|
||||
placeholderValue: 'Sélectionner les étiquettes à ajouter',
|
||||
allowHTML: true,
|
||||
delimiter: ',',
|
||||
removeItemButton: true,
|
||||
shouldSort: false,
|
||||
callbackOnCreateTemplates: () => (show_firstgroup)
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
@ -0,0 +1,51 @@
|
||||
{% extends "agenda_culturel/page-admin.html" %}
|
||||
{% load static %}
|
||||
{% load honeypot %}
|
||||
|
||||
{% block title %}{% block og_title %}{% if form.event %}Contact au sujet de l'événement {{ form.event.title }}{% else %}
|
||||
Contact{% endif %}{% endblock %}{% endblock %}
|
||||
|
||||
{% block entete_header %}
|
||||
<script>window.CKEDITOR_BASEPATH = '/static/ckeditor/ckeditor/';</script>
|
||||
<script src="/static/admin/js/vendor/jquery/jquery.js"></script>
|
||||
<script src="/static/admin/js/jquery.init.js"></script>
|
||||
<link href="{% static 'css/django_better_admin_arrayfield.min.css' %}" type="text/css" media="all" rel="stylesheet">
|
||||
<script src="{% static 'js/django_better_admin_arrayfield.min.js' %}"></script>
|
||||
<script src="{% static 'js/adjust_datetimes.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block fluid %}{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
|
||||
|
||||
<article>
|
||||
<header>
|
||||
<h1>{% if form.event %}Contact au sujet de l'événement « {{ form.event.title }} »{% else %}
|
||||
Contact{% endif %}</h1>
|
||||
|
||||
{% if not form.event %}
|
||||
<article>
|
||||
{% url 'contact' as local_url %}
|
||||
{% include "agenda_culturel/static_content.html" with name="contact" url_path=local_url %}
|
||||
</article>
|
||||
{% endif %}
|
||||
|
||||
<p class="message warning"><strong>Attention :</strong> n'utilisez pas le formulaire ci-dessous pour proposer un événement, il sera ignoré. Utilisez plutôt la page <a href="{% url 'add_event' %}">ajouter un événement</a>.</p>
|
||||
{% if form.event %}
|
||||
<p>Tu nous contactes au sujet de l'événement « {{ form.event.title }} » du {{ form.event.start_day }}.
|
||||
N'hésites pas à nous indiquer le maximum de contexte et à nous laisser ton adresse
|
||||
afin que l'on puisse répondre à tes demandes ou remarques.
|
||||
</p>
|
||||
{% endif %}
|
||||
</header>
|
||||
<form method="post">{% csrf_token %}
|
||||
{% render_honeypot_field "alias_name" %}
|
||||
{{ form.media }}
|
||||
{{ form.as_p }}
|
||||
<input type="submit" value="Envoyer">
|
||||
</form>
|
||||
</article>
|
||||
|
||||
{% endblock %}
|
@ -24,17 +24,18 @@
|
||||
<article>
|
||||
<header>
|
||||
<div class="slide-buttons">
|
||||
<a href="{% url 'delete_contactmessage' object.id %}" role="button" data-tooltip="Supprimer le message">Supprimer {% picto_from_name "trash-2" %}</a>
|
||||
<a href="{% url 'delete_message' object.id %}" role="button" data-tooltip="Supprimer le message">Supprimer {% picto_from_name "trash-2" %}</a>
|
||||
</div>
|
||||
|
||||
<h1>Modération du message « {{ object.subject }} »</h1>
|
||||
<ul>
|
||||
<li>Date : {{ object.date }}</li>
|
||||
<li>Auteur : {{ object.name }} <a href="mailto:{{ object.email }}">{{ object.email }}</a></li>
|
||||
<li>Date : {{ object.date.date }} à {{ object.date.time }}</li>
|
||||
<li>Auteur : {% if object.user %}<em>{{ object.user }}</em>{% else %}{{ object.name }}{% endif %} {% if object.email %}<a href="mailto:{{ object.email }}">{{ object.email }}</a>{% endif %}</li>
|
||||
{% if object.related_event %}<li>Événement associé : <a href="{{ object.related_event.get_absolute_url }}">{{ object.related_event.title }}</a> du {{ object.related_event.start_day }}</li>{% endif %}
|
||||
</ul>
|
||||
</header>
|
||||
<div>
|
||||
{{ object.message }}
|
||||
{{ object.message | safe }}
|
||||
</div>
|
||||
</article>
|
||||
|
||||
@ -46,7 +47,7 @@
|
||||
</article>
|
||||
</div>
|
||||
|
||||
{% include "agenda_culturel/side-nav.html" with current="contactmessages" %}
|
||||
{% include "agenda_culturel/side-nav.html" with current="messages" %}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
@ -26,7 +26,8 @@
|
||||
<h1>Derniers messages de contact reçus</h1>
|
||||
<form method="get" class="form django-form recent messages">
|
||||
{{ filter.form.as_div }}<br />
|
||||
<button type="submit">Filtrer</button><br />
|
||||
<button type="submit">Filtrer</button>
|
||||
</form>
|
||||
</header>
|
||||
|
||||
<table role="grid">
|
||||
@ -35,6 +36,7 @@
|
||||
<th>Date</th>
|
||||
<th>Sujet</th>
|
||||
<th>Auteur</th>
|
||||
<th>Événement</th>
|
||||
<th>Fermé</th>
|
||||
<th>Spam</th>
|
||||
</tr>
|
||||
@ -43,8 +45,9 @@
|
||||
{% for obj in paginator_filter %}
|
||||
<tr>
|
||||
<td>{{ obj.date }}</td>
|
||||
<td><a href="{% url 'contactmessage' obj.pk %}">{{ obj.subject }}</a></td>
|
||||
<td>{{ obj.name }}</td>
|
||||
<td><a href="{% url 'message' obj.pk %}">{{ obj.subject }}</a></td>
|
||||
<td>{% if obj.user %}<em>{{ obj.user }}</em>{% else %}{{ obj.name }}{% endif %}</td>
|
||||
<td>{% if obj.related_event %}<a href="{{ obj.related_event.get_absolute_url }}">{{ obj.related_event.pk }}</a>{% else %}/{% endif %}</td>
|
||||
<td>{% if obj.closed %}{% picto_from_name "check-square" "fermé" %}{% else %}{% picto_from_name "square" "ouvert" %}{% endif %}</td>
|
||||
<td>{% if obj.spam %}{% picto_from_name "check-square" "spam" %}{% else %}{% picto_from_name "square" "non spam" %}{% endif %}</td>
|
||||
</tr>
|
||||
@ -56,7 +59,7 @@
|
||||
</footer>
|
||||
</article>
|
||||
|
||||
{% include "agenda_culturel/side-nav.html" with current="contactmessages" %}
|
||||
{% include "agenda_culturel/side-nav.html" with current="messages" %}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
@ -1,20 +0,0 @@
|
||||
{% extends "agenda_culturel/page-admin.html" %}
|
||||
|
||||
{% block title %}{% block og_title %}Supprimer la réponse #{{ object.pk }}{% endblock %}{% endblock %}
|
||||
|
||||
{% block fluid %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h1>Suppression de la réponse de modération {{ object.pk }}</h1>
|
||||
<form method="post">{% csrf_token %}
|
||||
<p>Êtes-vous sûr·e de vouloir supprimer la réponse de modération #{{object.pk}} « {{ object.answer }} » associée à la question « {{ object.question.question }} » ?
|
||||
</p>
|
||||
{{ form }}
|
||||
<div class="grid buttons">
|
||||
<a href="{% if request.META.HTTP_REFERER %}{{ request.META.HTTP_REFERER }}{% else %}{{ object.get_absolute_url }}{% endif %}" role="button" class="secondary">Annuler</a>
|
||||
<input type="submit" value="Confirmer">
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
@ -1,31 +0,0 @@
|
||||
{% extends "agenda_culturel/page-admin.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block title %}{% block og_title %}{% if form.instance.pk %}Modification{% else %}Création{% endif %} d'une réponse de modération{% endblock %}{% endblock %}
|
||||
|
||||
{% block entete_header %}
|
||||
<script>window.CKEDITOR_BASEPATH = '/static/ckeditor/ckeditor/';</script>
|
||||
<script src="/static/admin/js/vendor/jquery/jquery.js"></script>
|
||||
<script src="/static/admin/js/jquery.init.js"></script>
|
||||
<link href="{% static 'css/django_better_admin_arrayfield.min.css' %}" type="text/css" media="all" rel="stylesheet">
|
||||
<script src="{% static 'js/django_better_admin_arrayfield.min.js' %}"></script>
|
||||
<script src="{% static 'js/adjust_datetimes.js' %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h1>{% if form.instance.pk %}Modification{% else %}Création{% endif %} d'une réponse de modération</h1>
|
||||
<p>{% if form.instance.pk %}Modifier{% else %}Ajouter{% endif %} une réponse à la question « {{ question }} »</p>
|
||||
|
||||
<article>
|
||||
<form method="post">{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<div class="grid buttons">
|
||||
<a href="{% if request.META.HTTP_REFERER %}{{ request.META.HTTP_REFERER }}{% else %}{% url 'view_mquestion' question.pk %}{% endif %}" role="button" class="secondary">Annuler</a>
|
||||
<input type="submit" value="Envoyer">
|
||||
</div>
|
||||
</form>
|
||||
</article>
|
||||
|
||||
{% endblock %}
|
@ -1,20 +0,0 @@
|
||||
{% extends "agenda_culturel/page-admin.html" %}
|
||||
|
||||
{% block title %}{% block og_title %}Supprimer la question #{{ object.pk }}{% endblock %}{% endblock %}
|
||||
|
||||
{% block fluid %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h1>Suppression de la question de modération {{ object.pk }}</h1>
|
||||
<form method="post">{% csrf_token %}
|
||||
<p>Êtes-vous sûr·e de vouloir supprimer la question de modération #{{object.pk}} « {{ object.question }} » ainsi que les réponses associées ?
|
||||
</p>
|
||||
{{ form }}
|
||||
<div class="grid buttons">
|
||||
<a href="{% if request.META.HTTP_REFERER %}{{ request.META.HTTP_REFERER }}{% else %}{{ object.get_absolute_url }}{% endif %}" role="button" class="secondary">Annuler</a>
|
||||
<input type="submit" value="Confirmer">
|
||||
</div>
|
||||
</form>
|
||||
|
||||
{% endblock %}
|
@ -1,71 +0,0 @@
|
||||
{% extends "agenda_culturel/page-admin.html" %}
|
||||
|
||||
{% block title %}{% block og_title %}Question de modération #{{ object.pk }}{% endblock %}{% endblock %}
|
||||
|
||||
{% load tag_extra %}
|
||||
{% load utils_extra %}
|
||||
{% load cat_extra %}
|
||||
|
||||
{% block entete_header %}
|
||||
{% css_categories %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block sidemenu-bouton %}
|
||||
<li><a href="#contenu-principal" aria-label="Aller au contenu">{% picto_from_name "chevron-up" %}</a></li>
|
||||
<li><a href="#sidebar" aria-label="Aller au menu latéral">{% picto_from_name "chevron-down" %}</a></li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="grid two-columns">
|
||||
<article>
|
||||
<header>
|
||||
<a href="{% url 'view_mquestions' %}" role="button">< Retour</a>
|
||||
<div class="slide-buttons">
|
||||
<a href="{% url 'delete_mquestion' object.pk %}" role="button">Supprimer</a>
|
||||
<a href="{% url 'edit_mquestion' object.pk %}" role="button">Modifier</a>
|
||||
<a href="{% url 'add_manswer' object.pk %}" role="button">Ajouter une réponse {% picto_from_name "plus-circle" %}</a>
|
||||
</div>
|
||||
<h1>Question de modération #{{ object.pk }}</h1>
|
||||
<p>{{ object.question }}</p>
|
||||
</header>
|
||||
|
||||
{% if object.answers %}
|
||||
{% for answer in object.answers.all %}
|
||||
<article>
|
||||
<div class="slide-buttons">
|
||||
<a href="{% url 'edit_manswer' object.pk answer.pk %}" role="button">Modifier</a>
|
||||
<a href="{% url 'delete_manswer' object.pk answer.pk %}" role="button">Supprimer</a>
|
||||
</div>
|
||||
<header>
|
||||
<h4><strong>Réponse #{{ answer.pk }} :</strong> « {{ answer.answer }} »</h4>
|
||||
{% if answer.adds_tags %}
|
||||
<p>Cette réponse ajoute les étiquettes suivantes à l'événement :
|
||||
{% for tag in answer.adds_tags %}
|
||||
{{ tag | tag_button }}
|
||||
{% endfor %}
|
||||
</p>
|
||||
{% else %}
|
||||
<p><em>Cette réponse n'ajoute pas d'étiquette à l'événement.</em></p>
|
||||
{% endif %}
|
||||
{% if answer.removes_tags %}
|
||||
<p>Cette réponse supprimer les étiquettes suivantes à l'événement :
|
||||
{% for tag in answer.removes_tags %}
|
||||
{{ tag | tag_button }}
|
||||
{% endfor %}
|
||||
</p>
|
||||
{% else %}
|
||||
<p><em>Cette réponse ne supprime pas d'étiquette à l'événement.</em></p>
|
||||
{% endif %}
|
||||
</header>
|
||||
</article>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
Il n'y a pas encore de réponse associée à cette question.
|
||||
{% endif %}
|
||||
</article>
|
||||
|
||||
{% include "agenda_culturel/side-nav.html" with current="moderationquestions" %}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
@ -1,23 +0,0 @@
|
||||
{% extends "agenda_culturel/page-admin.html" %}
|
||||
{% load static %}
|
||||
|
||||
|
||||
{% block title %}{% block og_title %}
|
||||
{% if form.instance.pk %}Modification{% else %}Création{% endif %} d'une question de modération
|
||||
{% endblock %}{% endblock %}
|
||||
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h1>{% if form.instance.pk %}Modification{% else %}Création{% endif %} d'une question de modération</h1>
|
||||
<article>
|
||||
<form method="post">{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<div class="grid buttons">
|
||||
<a href="{% if request.META.HTTP_REFERER %}{{ request.META.HTTP_REFERER }}{% else %}{{ object.get_absolute_url }}{% endif %}" role="button" class="secondary">Annuler</a>
|
||||
<input type="submit" value="Envoyer">
|
||||
</div>
|
||||
</form>
|
||||
</article>
|
||||
|
||||
{% endblock %}
|
@ -1,71 +0,0 @@
|
||||
{% extends "agenda_culturel/page-admin.html" %}
|
||||
|
||||
{% block title %}{% block og_title %}Questions de modération{% endblock %}{% endblock %}
|
||||
|
||||
{% load utils_extra %}
|
||||
{% load cat_extra %}
|
||||
{% block entete_header %}
|
||||
{% css_categories %}
|
||||
{% endblock %}
|
||||
|
||||
{% block sidemenu-bouton %}
|
||||
<li><a href="#contenu-principal" aria-label="Aller au contenu">{% picto_from_name "chevron-up" %}</a></li>
|
||||
<li><a href="#sidebar" aria-label="Aller au menu latéral">{% picto_from_name "chevron-down" %}</a></li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="grid two-columns">
|
||||
<article>
|
||||
<header>
|
||||
<a class="slide-buttons" href="{% url 'add_mquestion'%}" role="button">Ajouter {% picto_from_name "plus-circle" %}</a>
|
||||
<h1>Questions de modération</h1>
|
||||
</header>
|
||||
{% if object_list %}
|
||||
{% for question in object_list %}
|
||||
<article>
|
||||
<header>
|
||||
<a class="slide-buttons" href="{{ question.get_absolute_url }}" role="button">Détails...</a>
|
||||
<h2>Question #{{ question.pk }} : {{ question.question }}</h2>
|
||||
<p>{% if question.answers %}
|
||||
<p>réponses possibles :</p>
|
||||
<ul>
|
||||
{% for answer in question.answers.all %}
|
||||
<li>{{ answer.answer }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p><em>aucune réponse définie</em></p>
|
||||
{% endif %}
|
||||
</p>
|
||||
</header>
|
||||
|
||||
</article>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<p>Il n'y a aucune question définie.</p>
|
||||
{% endif %}
|
||||
<footer>
|
||||
<span>
|
||||
{% if page_obj.has_previous %}
|
||||
<a href="?page=1" role="button">« premier</a>
|
||||
<a href="?page={{ page_obj.previous_page_number }}" role="button">précédent</a>
|
||||
{% endif %}
|
||||
|
||||
<span>
|
||||
Page {{ page_obj.number }} sur {{ page_obj.paginator.num_pages }}
|
||||
</span>
|
||||
|
||||
{% if page_obj.has_next %}
|
||||
<a href="?page={{ page_obj.next_page_number }}" role="button">suivant</a>
|
||||
<a href="?page={{ page_obj.paginator.num_pages }}" role="button">dernier »</a>
|
||||
{% endif %}
|
||||
</span>
|
||||
</footer>
|
||||
|
||||
</article>
|
||||
|
||||
|
||||
{% include "agenda_culturel/side-nav.html" with current="moderationquestions" %}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
@ -4,9 +4,11 @@
|
||||
<a href="?page={{ page_obj.previous_page_number }}" role="button">précédent</a>
|
||||
{% endif %}
|
||||
|
||||
{% if page_obj.paginator.num_pages != 1 %}
|
||||
<span>
|
||||
Page {{ page_obj.number }} sur {{ page_obj.paginator.num_pages }}
|
||||
</span>
|
||||
{% endif %}
|
||||
|
||||
{% if page_obj.has_next %}
|
||||
<a href="?page={{ page_obj.next_page_number }}" role="button">suivant</a>
|
||||
|
@ -0,0 +1,25 @@
|
||||
{% extends "agenda_culturel/page.html" %}
|
||||
|
||||
{% block title %}{% block og_title %}Supprimer l'organisateur {{ object.name }}{% endblock %}{% endblock %}
|
||||
|
||||
{% block fluid %}{% endblock %}
|
||||
|
||||
{% block configurer-bouton %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<article>
|
||||
<header>
|
||||
<h1>Supprimer l'organisateur {{ object.name }}</h1>
|
||||
</header>
|
||||
<form method="post">{% csrf_token %}
|
||||
<p>Êtes-vous sûr·e de vouloir supprimer l'organisateur « {{ object.name }} ({{ object.pk }}) » ?</p>
|
||||
{{ form }}
|
||||
<div class="grid buttons">
|
||||
<a href="{% if request.META.HTTP_REFERER %}{{ request.META.HTTP_REFERER }}{% else %}{{ object.get_absolute_url }}{% endif %}" role="button" class="secondary">Annuler</a>
|
||||
<input type="submit" value="Confirmer">
|
||||
</div>
|
||||
</form>
|
||||
</article>
|
||||
|
||||
{% endblock %}
|
@ -0,0 +1,95 @@
|
||||
{% extends "agenda_culturel/page.html" %}
|
||||
|
||||
{% block title %}{% block og_title %}{{ object.name }}{% endblock %}{% endblock %}
|
||||
|
||||
{% load tag_extra %}
|
||||
{% load utils_extra %}
|
||||
{% load cat_extra %}
|
||||
{% load static %}
|
||||
{% load cache %}
|
||||
{% load l10n %}
|
||||
|
||||
{% block entete_header %}
|
||||
{% css_categories %}
|
||||
<script src="/static/admin/js/vendor/jquery/jquery.js"></script>
|
||||
<script src="/static/admin/js/jquery.init.js"></script>
|
||||
<script src="{% static 'location_field/leaflet/leaflet.js' %}"></script>
|
||||
<link href="{% static 'location_field/leaflet/leaflet.css' %}" type="text/css" media="all" rel="stylesheet">
|
||||
{% endblock %}
|
||||
|
||||
{% block fluid %}{% endblock %}
|
||||
|
||||
{% block body-class %}organisation{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
<article>
|
||||
<header>
|
||||
<a href="{% url 'view_organisations' %}" role="button">{% picto_from_name "chevron-left" %} Toutes les organisations</a>
|
||||
{% if perms.agenda_culturel.change_organisation %}
|
||||
<div class="slide-buttons">
|
||||
<a href="{% url 'edit_organisation' object.pk %}" role="button">Modifier {% picto_from_name "edit-3" %}</a>
|
||||
<a href="{% url 'delete_organisation' object.pk %}" role="button">Supprimer {% picto_from_name "trash-2" %}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
<h1>{{ object.name }}</h1>
|
||||
{% if object.website or object.principal_place %}
|
||||
<ul>
|
||||
{% if object.website %}
|
||||
<li><strong>Site internet :</strong> <a href="{{ object.website }}">{{ object.website }}</a></li>
|
||||
{% endif %}
|
||||
{% if object.principal_place %}
|
||||
<li><strong>Lieu principal :</strong> <a href="{{ object.principal_place.get_absolute_url }}">{{ object.principal_place }}</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% if user.is_authenticated %}
|
||||
{% if object.recurrentimport_set.all|length == 1 %}
|
||||
<p>Cet organisateur est associé à l'import récurrent <a href="{{ object.recurrentimport_set.all.0.get_absolute_url }}">{{ object.recurrentimport_set.all.0 }}</a>.</p>
|
||||
{% endif %}
|
||||
{% if object.recurrentimport_set.count > 1 %}
|
||||
<p>Cet organisateur est associé aux imports récurrents :</p>
|
||||
<ul>
|
||||
{% for ir in object.recurrentimport_set.all %}
|
||||
<li><a href="{{ ir.get_absolute_url }}">{{ ir }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{{ object.description|safe }}
|
||||
</header>
|
||||
|
||||
{% with cache_timeout=user.is_authenticated|yesno:"30,600" %}
|
||||
{% cache cache_timeout organisation_list user.is_authenticated object page_obj.number past %}
|
||||
<div class="slide-buttons">
|
||||
{% if past %}
|
||||
<a href="{{ object.get_absolute_url }}" role="button">Voir les événements à venir</a>
|
||||
{% else %}
|
||||
<a href="{% url 'view_organisation_past' object.pk %}" role="button">Voir les événements passés</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if past %}
|
||||
<h2>Événements passés</h2>
|
||||
{% else %}
|
||||
<h2>Événements à venir</h2>
|
||||
{% endif %}
|
||||
{% if object_list %}
|
||||
|
||||
{% include "agenda_culturel/navigation.html" with page_obj=page_obj %}
|
||||
|
||||
{% for event in object_list %}
|
||||
{% include "agenda_culturel/single-event/event-elegant-inc.html" with event=event day=0 no_location=1 %}
|
||||
{% endfor %}
|
||||
|
||||
{% include "agenda_culturel/navigation.html" with page_obj=page_obj %}
|
||||
|
||||
{% else %}
|
||||
<p><em>Aucun événement</em></p>
|
||||
{% endif %}
|
||||
{% endcache %}
|
||||
{% endwith %}
|
||||
<footer>
|
||||
</footer>
|
||||
</article>
|
||||
|
||||
{% endblock %}
|
@ -0,0 +1,43 @@
|
||||
{% extends "agenda_culturel/page-admin.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block entete_header %}
|
||||
<script src="{% url 'jsi18n' %}"></script>
|
||||
<script src="/static/admin/js/vendor/jquery/jquery.js"></script>
|
||||
<script src="/static/admin/js/jquery.init.js"></script>
|
||||
<script src="{% static 'choicejs/choices.min.js' %}"></script>
|
||||
<script>window.CKEDITOR_BASEPATH = '/static/ckeditor/ckeditor/';</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block title %}{% block og_title %}{% if object %}Description de {{ object.name }}{% else %}Description d'un organisme{% endif %}{% endblock %}{% endblock %}
|
||||
|
||||
{% block fluid %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<article>
|
||||
<header>
|
||||
<h1>{% if object %}Description de {{ object.name }}{% else %}Description d'un organisme{% endif %}</h1>
|
||||
</header>
|
||||
|
||||
<form method="post">{% csrf_token %}
|
||||
{{ form.media }}
|
||||
{{ form.as_p }}
|
||||
|
||||
<div class="grid buttons">
|
||||
<a href="{% if request.META.HTTP_REFERER %}{{ request.META.HTTP_REFERER }}{% else %}{% if object %}{{ object.get_absolute_url }}{% else %}{% url 'view_organisations' %}{% endif %}{% endif %}" role="button" class="secondary">Annuler</a>
|
||||
<input type="submit" value="Envoyer">
|
||||
</div>
|
||||
</form>
|
||||
</article>
|
||||
|
||||
<script>
|
||||
const places = document.querySelector('#id_principal_place');
|
||||
const choices_places = new Choices(places,
|
||||
{
|
||||
shouldSort: false,
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
{% endblock %}
|
@ -0,0 +1,76 @@
|
||||
{% extends "agenda_culturel/page.html" %}
|
||||
|
||||
{% block title %}{% block og_title %}Liste des organismes{% endblock %}{% endblock %}
|
||||
|
||||
{% block fluid %}{% endblock %}
|
||||
|
||||
{% load utils_extra %}
|
||||
{% load cat_extra %}
|
||||
{% block entete_header %}
|
||||
{% css_categories %}
|
||||
{% endblock %}
|
||||
|
||||
{% block sidemenu-bouton %}
|
||||
<li><a href="#contenu-principal" aria-label="Aller au contenu">{% picto_from_name "chevron-up" %}</a></li>
|
||||
<li><a href="#sidebar" aria-label="Aller au menu latéral">{% picto_from_name "chevron-down" %}</a></li>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<article>
|
||||
<header>
|
||||
{% if user.is_authenticated %}
|
||||
<div class="slide-buttons">
|
||||
<a href="{% url 'add_organisation' %}" role="button">Ajouter {% picto_from_name "plus-circle" %}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
<h1>Liste des organismes</h1>
|
||||
</header>
|
||||
{% include "agenda_culturel/navigation.html" with page_obj=page_obj %}
|
||||
</article>
|
||||
|
||||
{% if object_list %}
|
||||
{% for organisation in object_list %}
|
||||
<article>
|
||||
<header>
|
||||
{% if user.is_authenticated %}
|
||||
<div class="slide-buttons">
|
||||
<a href="{% url 'edit_organisation' organisation.pk %}" role="button">Modifier {% picto_from_name "edit-3" %}</a>
|
||||
<a href="{% url 'delete_organisation' organisation.pk %}" role="button">Supprimer {% picto_from_name "trash-2" %}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
<h2>{{ organisation.name }}</h2>
|
||||
</header>
|
||||
{% if organisation.website or organisation.principal_place %}
|
||||
<ul>
|
||||
{% if organisation.website %}
|
||||
<li><strong>Site internet :</strong> <a href="{{ organisation.website }}">{{ organisation.website }}</a></li>
|
||||
{% endif %}
|
||||
{% if organisation.principal_place %}
|
||||
<li><strong>Lieu principal :</strong> <a href="{{ organisation.principal_place.get_absolute_url }}">{{ organisation.principal_place }}</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
<footer>
|
||||
{% if organisation.organised_events.all|length > 1 %}
|
||||
<p class="slide-buttons"><a role="button" href="{{ organisation.get_absolute_url }}">voir les {{ organisation.organised_events.all|length }} événements {% picto_from_name "chevron-right" %}</a></p>
|
||||
{% else %}
|
||||
<p class="slide-buttons"><a role="button" href="{{ organisation.get_absolute_url }}">voir les détails {% picto_from_name "chevron-right" %}</a></p>
|
||||
{% endif %}
|
||||
<div style="clear: both"></div>
|
||||
</footer>
|
||||
</article>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<p>Il n'y a aucun organisme défini.</p>
|
||||
{% endif %}
|
||||
|
||||
<article>
|
||||
<footer>
|
||||
{% include "agenda_culturel/navigation.html" with page_obj=page_obj %}
|
||||
</footer>
|
||||
|
||||
</article>
|
||||
|
||||
|
||||
{% endblock %}
|
@ -3,10 +3,11 @@
|
||||
{% load cat_extra %}
|
||||
{% load utils_extra %}
|
||||
{% load event_extra %}
|
||||
{% load cache %}
|
||||
|
||||
|
||||
{% block title %}{% block og_title %}{{ event.title }}{% endblock %}{% endblock %}
|
||||
{% block og_image %}{% if event.has_image_url %}{{ event.get_image_url }}{% else %}{{ block.super }}{% endif %}{% endblock %}
|
||||
{% block og_image %}{% if event.has_image_url %}{{ event|get_image_uri:request }}{% else %}{{ block.super }}{% endif %}{% endblock %}
|
||||
{% block og_description %}{% if event.description %}{{ event.description |truncatewords:20|linebreaks }}{% else %}{{ block.super }}{% endif %}{% endblock %}
|
||||
|
||||
{% block entete_header %}
|
||||
@ -16,21 +17,59 @@
|
||||
|
||||
{% block content %}
|
||||
|
||||
|
||||
|
||||
<div class="grid two-columns">
|
||||
<div>
|
||||
{% with cache_timeout=user.is_authenticated|yesno:"30,600" %}
|
||||
{% cache cache_timeout event_body user.is_authenticated event %}
|
||||
{% include "agenda_culturel/single-event/event-single-inc.html" with event=event filter=filter %}
|
||||
{% endcache %}
|
||||
{% endwith %}
|
||||
|
||||
{% if user.is_authenticated %}
|
||||
<article>
|
||||
<article id="chronology">
|
||||
<header>
|
||||
<h2>Informations internes</h2>
|
||||
<h2>Chronologie</h2>
|
||||
</header>
|
||||
{% include "agenda_culturel/event-info-inc.html" with object=event %}
|
||||
{% for step in event.chronology %}
|
||||
{% if step.is_date %}
|
||||
<div class="entree dateline">
|
||||
<div><span class="ts">{{ step.timestamp }}</span></div>
|
||||
<div>
|
||||
{% if step.data == "created_date" %}<em>création</em>{% if event.created_by_user %} par {{ event.created_by_user.username }}{% endif %}{% endif %}
|
||||
{% if step.data == "modified_date" %}<em>dernière modification</em>{% if event.modified_by_user %} par {{ event.modified_by_user.username }}{% endif %}{% endif %}
|
||||
{% if step.data == "moderated_date" %}<em>dernière modération</em>{% if event.moderated_by_user %} par {{ event.moderated_by_user.username }}{% endif %}{% endif %}
|
||||
{% if step.data == "imported_date" %}<em>dernière importation</em>{% if event.imported_by_user %} par {{ event.imported_by_user.username }}{% endif %}{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="entree">
|
||||
<div><span class="ts">{{ step.timestamp }}</span></div>
|
||||
<div>
|
||||
<header><strong>Message</strong>{% if step.data.related_event and event != step.data.related_event %} sur <a href="{{ step.data.related_event.get_absolute_url }}">une autre</a> version{% endif %} : <a href="{{ step.data.get_absolute_url }}">{{ step.data.subject|truncatechars:20 }}</a> {% if step.data.user %} par <em>{{ step.data.user }}</em>{% else %} par {{ step.data.name }} (<a href="mailto: {{ step.data.email }}">{{ step.data.email }}</a>){% endif %}</header>
|
||||
<div class="texte">{{ step.data.message|safe }}</div>
|
||||
{% if step.data.comments %}
|
||||
<div><strong>Commentaire :</strong> {{ step.data.comments }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<form method="post">{% csrf_token %}
|
||||
{{ form.media }}
|
||||
{{ form.as_p }}
|
||||
<input type="submit" value="Commenter">
|
||||
</form>
|
||||
|
||||
{% include "agenda_culturel/event-info-inc.html" with allbutdates=1 %}
|
||||
</article>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
{% with cache_timeout=user.is_authenticated|yesno:"30,600" %}
|
||||
{% cache cache_timeout event_aside user.is_authenticated event %}
|
||||
<aside>
|
||||
{% with event.get_concurrent_events as concurrent_events %}
|
||||
{% if concurrent_events %}
|
||||
@ -51,39 +90,7 @@
|
||||
</article>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
<article>
|
||||
{% with event.get_nb_events_same_dates as nb_events_same_dates %}
|
||||
{% with nb_events_same_dates|length as c_dates %}
|
||||
<header>
|
||||
<h2>Voir aussi</h2>
|
||||
{% if c_dates != 1 %}
|
||||
<p class="remarque">
|
||||
Retrouvez ci-dessous tous les événements
|
||||
{% if event.is_single_day %}
|
||||
à la même date
|
||||
{% else %}
|
||||
aux mêmes dates
|
||||
{% endif %}
|
||||
que l'événement affiché.
|
||||
</p>
|
||||
{% endif %}
|
||||
</header>
|
||||
<nav>
|
||||
{% if c_dates == 1 %}
|
||||
<a role="button" href="{% url 'day_view' nb_events_same_dates.0.1.year nb_events_same_dates.0.1.month nb_events_same_dates.0.1.day %}">Toute la journée</a>
|
||||
{% else %}
|
||||
<ul>
|
||||
{% for nbevents_date in nb_events_same_dates %}
|
||||
<li>
|
||||
<a href="{% url 'day_view' nbevents_date.1.year nbevents_date.1.month nbevents_date.1.day %}">{{ nbevents_date.0 }} événement{{ nbevents_date.0 | pluralize }} le {{ nbevents_date.1 }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</nav>
|
||||
{% endwith %}
|
||||
{% endwith %}
|
||||
</article>
|
||||
|
||||
{% if event.other_versions and not event.other_versions.fixed %}
|
||||
{% with poss_dup=event.get_other_versions|only_allowed:user.is_authenticated %}
|
||||
{% if poss_dup|length > 0 %}
|
||||
@ -122,12 +129,15 @@
|
||||
{% else %}
|
||||
Signaler comme doublon
|
||||
{% endif %}</a>
|
||||
</article>
|
||||
<a role="button" href="{% url 'message_for_event' event.pk %}">Signaler cet événement</a>
|
||||
</article>
|
||||
|
||||
|
||||
</aside>
|
||||
|
||||
{% endcache %}
|
||||
{% endwith %}
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
{% endblock %}
|
@ -1,9 +1,6 @@
|
||||
{% extends "agenda_culturel/page.html" %}
|
||||
|
||||
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% load cat_extra %}
|
||||
{% load event_extra %}
|
||||
{% load utils_extra %}
|
||||
@ -25,12 +22,11 @@
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
{% with cache_timeout=user.is_authenticated|yesno:"30,600" %}
|
||||
{% cache cache_timeout month user.is_authenticated calendar.firstdate filter.to_str LANGUAGE_CODE %}
|
||||
|
||||
{% include "agenda_culturel/filter-inc.html" with filter=filter noarticle=0 %}
|
||||
|
||||
{% with cache_timeout=user.is_authenticated|yesno:"30,600" %}
|
||||
{% cache cache_timeout month user.is_authenticated calendar.firstdate filter.get_url %}
|
||||
|
||||
<article>
|
||||
<header>
|
||||
@ -64,12 +60,75 @@
|
||||
{% endif %}
|
||||
|
||||
<div class="grid week-in-month">
|
||||
{% for d in calendar.calendar_days_list %}
|
||||
{% for day in calendar.calendar_days_list %}
|
||||
{% if forloop.counter0|divisibleby:7 %}
|
||||
{% if not forloop.first %}</div><div class="grid week-in-month">{% endif %}
|
||||
<div id="week-{{ d.date.isocalendar.1 }}" class="entete-semaine"><h2><a href="{% url 'week_view' calendar.year d.week %}?{{ filter.get_url }}">Semaine {{ d.week }}</a></h2></div>
|
||||
<div id="week-{{ day.date.isocalendar.1 }}" class="entete-semaine"><h2><a href="{% url 'week_view' calendar.year day.week %}?{{ filter.get_url }}">Semaine {{ d.week }}</a></h2></div>
|
||||
{% endif %}
|
||||
{% include "agenda_culturel/day-inc.html" with day=d resume=1 fixed_style=calendar.all_in_past filter=filter headers="h3" %}
|
||||
|
||||
|
||||
|
||||
{% with day.date|date:"Y-m-d" as daytag %}
|
||||
{% with "date-"|add:daytag as daytag %}
|
||||
|
||||
<article class="day{{ day|calendar_classes:calendar.all_in_past }}" id="{{ daytag }}">
|
||||
{% if day.is_today %}
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function(e) {
|
||||
agenda = document.getElementById("calendar");
|
||||
todayArticle = document.getElementById("today").parentElement;
|
||||
left = todayArticle.offsetLeft;
|
||||
top = todayArticle.offsetTop;
|
||||
agenda.scrollTo({top: top, left: left});
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
<header{% if day.is_today %} id="today"{% endif %}>
|
||||
<h3><a href="{{ day.date | url_day }}?{{ filter.get_url }}">{{ day.date | date:"l j" }}</a></h3}>
|
||||
</header>
|
||||
{% if day.events %}
|
||||
<ul>
|
||||
{% for category, events in day.events_by_category_ordered %}
|
||||
<li>{{ events.0.category | circle_cat }}
|
||||
<a href="{{ day.date | url_day }}?{{ filter.get_url|add_url_category:events.0.category }}" data-target="{{ daytag }}-category-{{ events.0.category.pk }}" onClick="toggleModal(event)">{{ events | length }} {{ category }}</a></li>
|
||||
<dialog id="{{ daytag }}-category-{{ events.0.category.pk }}">
|
||||
<article>
|
||||
<header>
|
||||
<a href="#{{ daytag }}-category-{{ events.0.category.pk }}"
|
||||
aria-label="Fermer"
|
||||
class="close"
|
||||
data-target="{{ daytag }}-category-{{ events.0.category.pk }}"
|
||||
onClick="toggleModal(event)"></a>
|
||||
<h3>{{ events.0.category | small_cat }} du {{ day.date | date:"l j F" }}</h3>
|
||||
</header>
|
||||
<ul>
|
||||
{% for event in events %}
|
||||
<li>
|
||||
{% if event.start_day == day.date and event.start_time %}
|
||||
{{ event.start_time }}
|
||||
{% endif %}
|
||||
<a href="{{ event.get_absolute_url }}">{{ event|picto_status }} {{ event.title|no_emoji }} {{ event|tw_badge }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<footer>
|
||||
<div class="buttons">
|
||||
<a href="{{ day.date | url_day }}?{{ filter.get_url }}" role="button">Voir la journée <svg width="1em" height="1em" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<use href="{% static 'images/feather-sprite.svg' %}#chevron-right" />
|
||||
</svg></a>
|
||||
</div>
|
||||
</footer>
|
||||
</article>
|
||||
</dialog>
|
||||
{% endfor %}
|
||||
<ul>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</article>
|
||||
|
||||
{% endwith %}
|
||||
{% endwith %}
|
||||
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% if calendar.lastdate|shift_day:+1|not_after_last %}
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
{% load cat_extra %}
|
||||
{% load utils_extra %}
|
||||
{% load tag_extra %}
|
||||
|
||||
{% block entete_header %}
|
||||
{% css_categories %}
|
||||
@ -20,7 +21,7 @@
|
||||
<header>
|
||||
<div class="buttons left-buttons">
|
||||
<a href="{% url 'recurrent_imports' %}" role="button">< Tous les imports récurrents</a>
|
||||
<a href="{% url 'moderation' %}?import_sources={{ object.pk }}" role="button">Voir les événements {% picto_from_name "calendar" %}</a>
|
||||
<a href="{% url 'recent' %}?import_sources={{ object.pk }}" role="button">Voir les événements {% picto_from_name "calendar" %}</a>
|
||||
</div>
|
||||
<div class="buttons slide-buttons">
|
||||
<a href="{% url 'run_rimport' object.pk %}" role="button">Exécuter {% picto_from_name "download-cloud" %}</a>
|
||||
@ -38,12 +39,13 @@
|
||||
<li><strong>Adresse naviguable :</strong> <a href="{{ object.browsable_url }}">{{ object.browsable_url }}</a></li>
|
||||
<li><strong>Valeurs par défaut :</strong>
|
||||
<ul>
|
||||
<li><strong>Publié :</strong> {{ object.defaultPublished }}</li>
|
||||
<li><strong>Localisation :</strong> {{ object.defaultLocation }}</li>
|
||||
<li><strong>Publié :</strong> {{ object.defaultPublished|yesno:"Oui,Non" }}</li>
|
||||
{% if object.defaultLocation %}<li><strong>Localisation :</strong> {{ object.defaultLocation }}</li>{% endif %}
|
||||
<li><strong>Catégorie :</strong> {{ object.defaultCategory }}</li>
|
||||
{% if object.defaultOrganiser %}<li><strong>Organisateur :</strong> <a href="{{ object.defaultOrganiser.get_absolute_url }}">{{ object.defaultOrganiser }}</a></li>{% endif %}
|
||||
<li><strong>Étiquettes :</strong>
|
||||
{% for tag in object.defaultTags %}
|
||||
{{ tag }}{% if not forloop.last %}, {% endif %}
|
||||
<a href="{% url 'view_tag' tag %}">{{ tag|tw_highlight }}</a>{% if not forloop.last %}, {% endif %}
|
||||
{% endfor %}
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -1,10 +1,12 @@
|
||||
{% extends "agenda_culturel/page-single.html" %}
|
||||
{% load static %}
|
||||
|
||||
{% block content %}
|
||||
<article>
|
||||
<header>
|
||||
<h1>{{ title }}</h1>
|
||||
</header>
|
||||
<div class="image-flottante"><img src="{% static 'images/alunissage.svg' %}" alt="deux pommes qui alunissent" /></div>
|
||||
{% include "agenda_culturel/static_content.html" with name=static_content url_path=url_path %}
|
||||
<ul>
|
||||
{% for rimport in rimports %}
|
||||
|
@ -1,7 +1,6 @@
|
||||
{% extends "agenda_culturel/page.html" %}
|
||||
|
||||
|
||||
{% load i18n %}
|
||||
{% load cache %}
|
||||
{% load cat_extra %}
|
||||
{% load event_extra %}
|
||||
@ -21,9 +20,6 @@
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
{% with cache_timeout=user.is_authenticated|yesno:"30,600" %}
|
||||
{% cache cache_timeout upcoming user.is_authenticated calendar.firstdate filter.to_str calendar.calendar_days_list|length LANGUAGE_CODE %}
|
||||
|
||||
<article id="filters">
|
||||
<header><h1 id="index-avenir">{% block title %}{% block og_title %}
|
||||
@ -40,6 +36,9 @@
|
||||
|
||||
{% include "agenda_culturel/filter-inc.html" with filter=filter noarticle=1 %}
|
||||
|
||||
{% with cache_timeout=user.is_authenticated|yesno:"30,600" %}
|
||||
{% cache cache_timeout upcoming user.is_authenticated calendar.firstdate filter.get_url calendar.calendar_days_list|length %}
|
||||
|
||||
<footer>
|
||||
<a href="{% if calendar.calendar_days_list|length == 1 %}{% url 'day_view' date_pred.year date_pred.month date_pred.day %}{% else %}{% url 'a_venir_jour' date_pred.year date_pred.month date_pred.day %}{% endif %}" aria-label="dates précédentes" role="button">
|
||||
{% picto_from_name "chevrons-left" %}
|
||||
@ -96,13 +95,19 @@
|
||||
<h3>{{ ti.short_name }} <a class="badge simple" href="#{{ ti.id }}" data-tooltip="Aller à {{ ti.name }}">{{ ti.events|length }} {% picto_from_name "chevrons-down" %}</a></h3>
|
||||
<ul>
|
||||
{% for event in ti.events %}
|
||||
{% if event.is_first_after_now %}
|
||||
<li class="line-now"><div><div>{% now "H:i" %}</div><div class="line"></div></div></li>
|
||||
{% endif %}
|
||||
<li>{{ event.category | circle_cat:event.has_recurrences }}
|
||||
{% if event.start_time %}
|
||||
{{ event.start_time }}
|
||||
{% endif %}
|
||||
{{ event|picto_status }} <a href="#event-{{ event.id }}">{{ event.title }}</a>
|
||||
{{ event|picto_status }} <a href="#event-{{ event.id }}">{{ event.title|no_emoji }}</a> {{ event|tw_badge }}
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% if forloop.last and cd.is_today_after_events %}
|
||||
<li class="line-now"><div><div>{% now "H:i" %}</div><div class="line"></div></div></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
@ -1,13 +1,12 @@
|
||||
{% extends "agenda_culturel/page.html" %}
|
||||
|
||||
|
||||
{% load i18n %}
|
||||
{% load cache %}
|
||||
{% load cat_extra %}
|
||||
{% load event_extra %}
|
||||
{% load utils_extra %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% load tag_extra %}
|
||||
|
||||
{% block entete_header %}
|
||||
{% css_categories %}
|
||||
@ -21,11 +20,10 @@
|
||||
|
||||
{% block content %}
|
||||
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
{% with cache_timeout=user.is_authenticated|yesno:"30,600" %}
|
||||
{% cache cache_timeout week user.is_authenticated calendar.firstdate filter.to_str LANGUAGE_CODE %}
|
||||
{% include "agenda_culturel/filter-inc.html" with filter=filter noarticle=0 %}
|
||||
|
||||
{% include "agenda_culturel/filter-inc.html" with filter=filter noarticle=0 %}
|
||||
{% with cache_timeout=user.is_authenticated|yesno:"30,600" %}
|
||||
{% cache cache_timeout week user.is_authenticated calendar.firstdate filter.get_url %}
|
||||
|
||||
<article>
|
||||
<header>
|
||||
@ -39,7 +37,7 @@
|
||||
<div>
|
||||
{% if calendar.firstdate|shift_day:-1|not_before_first %}
|
||||
{% if calendar.lastdate|not_after_last %}
|
||||
<a role="button" href="{% url 'week_view' calendar.previous_week.year calendar.previous_week|week %}?{{ filter.get_url }}">
|
||||
<a role="button" href="{% url 'week_view' calendar.previous_week|weekyear calendar.previous_week|week %}?{{ filter.get_url }}">
|
||||
{% picto_from_name "chevron-left" %} précédente</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
@ -47,7 +45,7 @@
|
||||
{% if calendar.lastdate|shift_day:+1|not_after_last %}
|
||||
{% if calendar.lastdate|not_before_first %}
|
||||
<div class="right">
|
||||
<a role="button" href="{% url 'week_view' calendar.next_week.year calendar.next_week|week %}?{{ filter.get_url }}">suivante
|
||||
<a role="button" href="{% url 'week_view' calendar.next_week|weekyear calendar.next_week|week %}?{{ filter.get_url }}">suivante
|
||||
{% picto_from_name "chevron-right" %}
|
||||
</a>
|
||||
</div>
|
||||
@ -59,19 +57,116 @@
|
||||
<div class="slider-button slider-button-inside button-left hidden">{% picto_from_name "arrow-left" %}</div>
|
||||
{% if calendar.firstdate|shift_day:-1|not_before_first %}
|
||||
{% if calendar.lastdate|not_after_last %}
|
||||
<div class="slider-button slider-button-page button-left hidden"><a href="{% url 'week_view' calendar.previous_week.year calendar.previous_week|week %}?{{ filter.get_url }}">{% picto_from_name "chevrons-left" %}</a></div>
|
||||
<div class="slider-button slider-button-page button-left hidden"><a href="{% url 'week_view' calendar.previous_week|weekyear calendar.previous_week|week %}?{{ filter.get_url }}">{% picto_from_name "chevrons-left" %}</a></div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
<div class="grid">
|
||||
{% for d in calendar.calendar_days_list %}
|
||||
{% include "agenda_culturel/day-inc.html" with day=d resume=0 fixed_style=calendar.all_in_past filter=filter headers="" %}
|
||||
{% for day in calendar.calendar_days_list %}
|
||||
|
||||
{% with day.date|date:"Y-m-d" as daytag %}
|
||||
{% with "date-"|add:daytag as daytag %}
|
||||
|
||||
<article class="day{{ day|calendar_classes:calendar.all_in_past }}" id="{{ daytag }}">
|
||||
{% if day.is_today %}
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function(e) {
|
||||
agenda = document.getElementById("calendar");
|
||||
todayArticle = document.getElementById("today").parentElement;
|
||||
left = todayArticle.offsetLeft;
|
||||
top = todayArticle.offsetTop;
|
||||
agenda.scrollTo({top: top, left: left});
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
<header{% if day.is_today %} id="today"{% endif %}>
|
||||
<h2><a href="{{ day.date | url_day }}?{{ filter.get_url }}">{{ day.date | date:"l j" }}</a></h2>
|
||||
</header>
|
||||
{% if day.events %}
|
||||
<ul>
|
||||
{% for event in day.events %}
|
||||
{% if event.is_first_after_now %}
|
||||
<li class="line-now"><div><div>{% now "H:i" %}</div><div class="line"></div></div></li>
|
||||
{% endif %}
|
||||
<li>{{ event.category | circle_cat:event.has_recurrences }}
|
||||
{% if event.start_day == day.date and event.start_time %}
|
||||
{{ event.start_time }}
|
||||
{% endif %}
|
||||
{{ event|picto_status }} <a href="{{ event.get_absolute_url }}" data-target="event-{{ event.id }}" onClick="toggleModal(event)">{{ event.title|no_emoji }}</a>
|
||||
{{ event|tw_badge }}
|
||||
<dialog id="event-{{ event.id }}">
|
||||
<article>
|
||||
<header>
|
||||
<a href="#event-{{ event.id }}"
|
||||
aria-label="Fermer"
|
||||
class="close"
|
||||
data-target="event-{{ event.id }}"
|
||||
onClick="toggleModal(event)"></a>
|
||||
<h3>{{ event.category|small_cat_recurrent:event.has_recurrences }} {{ event|picto_status }} {{ event.title }} {{ event|picto_visibility:user.is_authenticated }}</h3>
|
||||
|
||||
<p>
|
||||
{% picto_from_name "map-pin" %}
|
||||
|
||||
{% if event.exact_location %}
|
||||
{{ event.exact_location.name }}, {{ event.exact_location.city }}
|
||||
{% else %}
|
||||
{% if perms.agenda_culturel.change_event and perms.agenda_culturel.change_place %}
|
||||
<a href="{% url 'add_place_to_event' event.pk %}" class="missing-data">{{ event.location }}</a>
|
||||
{% else %}
|
||||
{{ event.location }}
|
||||
{% endif %}
|
||||
{% endif %} </p>
|
||||
<p>
|
||||
{% picto_from_name "calendar" %}
|
||||
{% if event.end_day and event.end_day != event.start_day %}du{% else %}le{% endif %}
|
||||
{{ event.start_day|date|frdate }}
|
||||
{% if event.start_time %} {% if not event.end_day or event.end_day == event.start_day %}{% if event.end_time %}de{% else %}à{% endif %}{% endif %}
|
||||
{{ event.start_time }}
|
||||
{% endif %}
|
||||
{% if event.end_day and event.end_day != event.start_day %}
|
||||
au {% if event.end_day and event.end_day != event.start_day %}{{ event.end_day|date|frdate }}{% endif %}
|
||||
{% endif %}
|
||||
{% if event.end_time %} {% if not event.end_day|date|frdate or event.end_day == event.start_day %}jusqu'à{% endif %} {{ event.end_time }}{% endif %}
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<div class="body-fixed">{{ event.description |linebreaks }}</div>
|
||||
|
||||
|
||||
<p>
|
||||
{% for tag in event.sorted_tags %}
|
||||
{{ tag | tag_button }}
|
||||
{% endfor %}
|
||||
</p>
|
||||
|
||||
<footer class="infos-and-buttons">
|
||||
<div class="buttons">
|
||||
<a href="{{ event.get_absolute_url }}" role="button">Voir l'événement <svg width="1em" height="1em" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<use href="{% static 'images/feather-sprite.svg' %}#chevron-right" />
|
||||
</svg></a>
|
||||
</div>
|
||||
</footer>
|
||||
</article>
|
||||
</dialog>
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% if day.is_today_after_events %}
|
||||
<li class="line-now"><div><div>{% now "H:i" %}</div><div class="line"></div></div></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</article>
|
||||
|
||||
{% endwith %}
|
||||
{% endwith %}
|
||||
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="slider-button slider-button-inside button-right hidden">{% picto_from_name "arrow-right" %}</div>
|
||||
{% if calendar.lastdate|shift_day:+1|not_after_last %}
|
||||
{% if calendar.lastdate|not_before_first %}
|
||||
<div class="slider-button slider-button-page button-right hidden"><a href="{% url 'week_view' calendar.next_week.year calendar.next_week|week %}?{{ filter.get_url }}">{% picto_from_name "chevrons-right" %}</a></div>
|
||||
<div class="slider-button slider-button-page button-right hidden"><a href="{% url 'week_view' calendar.next_week|weekyear calendar.next_week|week %}?{{ filter.get_url }}">{% picto_from_name "chevrons-right" %}</a></div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
|
@ -1,5 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
{% load event_extra %}
|
||||
{% load cache %}
|
||||
{% load messages_extra %}
|
||||
{% load utils_extra %}
|
||||
{% load duplicated_extra %}
|
||||
{% load rimports_extra %}
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
@ -9,7 +15,7 @@
|
||||
{% load static %}
|
||||
<meta property="og:title" content="Pommes de lune — {% block og_title %}{% endblock %}" />
|
||||
<meta property="og:description" content="{% block og_description %}Événements culturels à Clermont-Ferrand et aux environs{% endblock %}" />
|
||||
<meta property="og:image" content="{% block og_image %}{% static 'images/capture.png' %}{% endblock %}" />
|
||||
<meta property="og:image" content="{% block og_image %}https://{{ request.get_host }}{% get_media_prefix %}screenshot.png{% endblock %}" />
|
||||
<meta property="og:url" content="{{ request.build_absolute_uri }}" />
|
||||
|
||||
{% if debug %}
|
||||
@ -27,12 +33,7 @@
|
||||
{% block entete_header %}
|
||||
{% endblock %}
|
||||
</head>
|
||||
{% load event_extra %}
|
||||
{% load cache %}
|
||||
{% load contactmessages_extra %}
|
||||
{% load utils_extra %}
|
||||
{% load duplicated_extra %}
|
||||
{% load rimports_extra %}
|
||||
|
||||
<body class="{% block body-class %}contenu{% endblock %}">
|
||||
<div id="boutons-fixes">
|
||||
<ul>
|
||||
@ -50,7 +51,7 @@
|
||||
{% if user.is_authenticated %}{% block configurer-menu %}<li id="menu-configurer" class="configurer-bouton"><a href="{% url 'administration' %}">Administrer {% picto_from_name "settings" %}</a></li>{% endblock %}{% endif %}
|
||||
{% block ajouter-menu %}<li id="menu-ajouter" class="ajouter-bouton"><a href="{% url 'add_event' %}">Ajouter un événement {% picto_from_name "plus-circle" %}</a></li>{% endblock %}
|
||||
{% block rechercher-menu %}<li id="menu-rechercher" class="rechercher-bouton"><a href="{% url 'event_search' %}">Rechercher {% picto_from_name "search" %}</a></li>{% endblock %}
|
||||
<li><a href="{% url 'a_venir' %}{% block a_venir_parameters %}{% endblock %}">À venir</a></li>
|
||||
<li><a href="{% url 'a_venir' %}{% block a_venir_parameters %}{% endblock %}">Maintenant</a></li>
|
||||
<li><a href="{% url 'cette_semaine' %}{% block cette_semaine_parameters %}{% endblock %}">Cette semaine</a></li>
|
||||
<li><a href="{% url 'ce_mois_ci' %}{% block ce_mois_ci_parameters %}{% endblock %}">Ce mois-ci</a></li>
|
||||
</ul>
|
||||
@ -65,8 +66,7 @@
|
||||
<li>
|
||||
<div>
|
||||
{% if perms.agenda_culturel.view_recurrentimport %}
|
||||
{% show_badge_rimports "bottom" "failed" %}
|
||||
{% show_badge_rimports "bottom" "running" %}
|
||||
{% show_badges_rimports "bottom" %}
|
||||
{% endif %}
|
||||
{% if perms.agenda_culturel.change_event %}
|
||||
{% show_badges_events "bottom" %}
|
||||
@ -77,8 +77,8 @@
|
||||
{% if perms.agenda_culturel.change_place and perms.agenda_culturel.change_event %}
|
||||
{% show_badge_unknown_places "bottom" %}
|
||||
{% endif %}
|
||||
{% if perms.agenda_culturel.view_contactmessage %}
|
||||
{% show_badge_contactmessages "bottom" %}
|
||||
{% if perms.agenda_culturel.view_message %}
|
||||
{% show_badge_messages "bottom" %}
|
||||
{% endif %}
|
||||
{% if user.is_authenticated %}
|
||||
{{ user.username }} @
|
||||
@ -108,6 +108,9 @@
|
||||
<div>
|
||||
<a href="{% url 'view_places' %}">{% picto_from_name "map-pin" %} lieux</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="{% url 'view_organisations' %}">{% picto_from_name "users" %} organisateurs</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="{% url 'view_all_tags' %}">{% picto_from_name "tag" %} étiquettes</a>
|
||||
</div>
|
||||
|
@ -7,7 +7,6 @@
|
||||
{% load cat_extra %}
|
||||
{% load static %}
|
||||
{% load cache %}
|
||||
{% load i18n %}
|
||||
{% load l10n %}
|
||||
|
||||
{% block entete_header %}
|
||||
@ -49,6 +48,28 @@
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
</ul>
|
||||
|
||||
{% if object.description and not object.description|html_vide %}
|
||||
<h2>Description du lieu</h2>
|
||||
{{ object.description|safe }}
|
||||
{% endif %}
|
||||
|
||||
{% with object.organisation_set.all as organisations %}
|
||||
{% if organisations|length == 1 %}
|
||||
<p>L'organisme <a href="{{ organisations.0.get_absolute_url }}">{{ organisations.0 }}</a> organise régulièrement des événements dans ce lieu.</p>
|
||||
{% endif %}
|
||||
{% if organisations|length > 1 %}
|
||||
<p>Les organismes suivants utilisent régulièrement ce lieu :</p>
|
||||
<ul>
|
||||
{% for o in organisations %}
|
||||
<li><a href="{{ o.get_absolute_url }}">{{ o }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
|
||||
{% endwith %}
|
||||
|
||||
|
||||
</div>
|
||||
<div>
|
||||
<div id="map_location" style="width: 100%; aspect-ratio: 16/16"></div>
|
||||
@ -64,7 +85,6 @@
|
||||
<p>Voir aussi <a href="{% url 'view_places' %}">les autres lieux</a></p>
|
||||
</div>
|
||||
</div>
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
{% with cache_timeout=user.is_authenticated|yesno:"30,600" %}
|
||||
{% cache cache_timeout place_list user.is_authenticated object page_obj.number past %}
|
||||
<div class="slide-buttons">
|
||||
@ -74,12 +94,12 @@
|
||||
<a href="{% url 'view_place_past' object.pk %}" role="button">Voir les événements passés</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if past %}
|
||||
<h2>Événements passés</h2>
|
||||
{% else %}
|
||||
<h2>Événements à venir</h2>
|
||||
{% endif %}
|
||||
{% if object_list %}
|
||||
{% if past %}
|
||||
<h2>Événements passés</h2>
|
||||
{% else %}
|
||||
<h2>Événements à venir</h2>
|
||||
{% endif %}
|
||||
|
||||
{% include "agenda_culturel/navigation.html" with page_obj=page_obj %}
|
||||
|
||||
|
@ -22,16 +22,33 @@
|
||||
<article>
|
||||
{% if event %}
|
||||
<p>Création d'un lieu depuis l'événement « {{ event }} » (voir en bas de page le détail de l'événement).</p>
|
||||
<p><strong>Remarque :</strong> les champs ont été pré-remplis à partir de la description sous forme libre et n'est probablement pas parfaite.</p>
|
||||
{% endif %}
|
||||
<form method="post">{% csrf_token %}
|
||||
{{ form.as_grid }}
|
||||
<div class="grid form-place">
|
||||
{{ form }}
|
||||
<div class="map-widget">
|
||||
<div id="map_location" style="width: 100%; aspect-ratio: 16/9"></div>
|
||||
<p>Cliquez pour ajuster la position GPS</p>
|
||||
<input type="checkbox" role="switch" id="lock_position">Verrouiller la position</input>
|
||||
<script>
|
||||
document.getElementById("lock_position").onclick = function() {
|
||||
const field = document.getElementById("id_location");
|
||||
if (this.checked)
|
||||
field.setAttribute("readonly", true);
|
||||
else
|
||||
field.removeAttribute("readonly");
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid buttons">
|
||||
<a href="{% if request.META.HTTP_REFERER %}{{ request.META.HTTP_REFERER }}{% else %}{{ object.get_absolute_url }}{% endif %}" role="button" class="secondary">Annuler</a>
|
||||
<input type="submit" value="Envoyer">
|
||||
</div>
|
||||
</form>
|
||||
{% if event %}
|
||||
<h2>Description du lieu :</h2>
|
||||
<h2>Détails de l'événement :</h2>
|
||||
{% include "agenda_culturel/single-event/event-single-inc.html" with event=event noedit=1 %}
|
||||
{% endif %}
|
||||
</article>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user