Skip to content

Commit f8978ab

Browse files
committed
Merge branch 'master' into friends_improvements
2 parents 47819f8 + 5b1a146 commit f8978ab

31 files changed

+799
-20
lines changed

app/mixins.py

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -88,28 +88,52 @@ def is_read_only(self):
8888
return False
8989
return True
9090

91+
def __check_field_visibility(self, field, visible):
92+
if field.get('space', 0) <= 0:
93+
field['field'].field.widget = forms.HiddenInput()
94+
visible[field['field'].auto_id] = {
95+
self.fields.get(visible_name).get_bound_field(self, visible_name).html_name:
96+
([str(x) for x in values] if isinstance(values, list) else [str(values)])
97+
for visible_name, values in field.get('visible', {}).items()
98+
}
99+
100+
def __check_field_is_required(self, field):
101+
if field['field'].field.required:
102+
field['field'].label = mark_safe(field['field'].label + ' <span class="text-danger">*</span>')
103+
104+
def __add_field_api_settings(self, field, name):
105+
api_fields = getattr(getattr(self, 'Meta', None), 'api_fields', {})
106+
if name in api_fields:
107+
field['api'] = api_fields[name]
108+
if field['api'].get('restrict', False):
109+
field['field'].help_text += _("Please select one of the autocomplete options "
110+
"or write 'Others'.")
111+
112+
def __add_field_datetime_settings(self, field, name):
113+
field_instance = field['field'].field
114+
if isinstance(field_instance, forms.DateField):
115+
field['datetimepicker'] = {'type': 'DateField'}
116+
elif isinstance(field_instance, forms.DateTimeField):
117+
field['datetimepicker'] = {'type': 'DateTimeField'}
118+
elif isinstance(field_instance, forms.TimeField):
119+
field['datetimepicker'] = {'type': 'TimeField'}
120+
else:
121+
return None
122+
datetimepicker_fields = getattr(getattr(self, 'Meta', None), 'datetimepicker_fields', {})
123+
if name in datetimepicker_fields:
124+
field['datetimepicker'].update(datetimepicker_fields[name])
125+
91126
def get_fields(self):
92127
result = self.get_bootstrap_field_info()
93128
for list_fields in result.values():
94129
visible = {}
95130
for field in list_fields.get('fields', []):
96131
name = field.get('name')
97132
field.update({'field': self.fields.get(name).get_bound_field(self, name)})
98-
if field.get('space', 0) <= 0:
99-
field['field'].field.widget = forms.HiddenInput()
100-
visible[field['field'].auto_id] = {
101-
self.fields.get(visible_name).get_bound_field(self, visible_name).html_name:
102-
([str(x) for x in values] if isinstance(values, list) else [str(values)])
103-
for visible_name, values in field.get('visible', {}).items()
104-
}
105-
if field['field'].field.required:
106-
field['field'].label = mark_safe(field['field'].label + ' <span class="text-danger">*</span>')
107-
api_fields = getattr(getattr(self, 'Meta', None), 'api_fields', {})
108-
if name in api_fields:
109-
field['api'] = api_fields[name]
110-
if field['api'].get('restrict', False):
111-
field['field'].help_text += _("Please select one of the autocomplete options "
112-
"or write 'Others'.")
133+
self.__check_field_visibility(field, visible)
134+
self.__check_field_is_required(field)
135+
self.__add_field_api_settings(field, name)
136+
self.__add_field_datetime_settings(field, name)
113137
if not visible[field['field'].auto_id]:
114138
del visible[field['field'].auto_id]
115139
list_fields['visible'] = visible

app/settings.py

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,10 @@
6868
'user',
6969
'application',
7070
'review',
71+
'stats',
7172
'friends',
7273
'event',
73-
'stats',
74+
'event.messages',
7475
'event.meals',
7576
]
7677

@@ -433,3 +434,21 @@
433434

434435
# Disposable email token from https://api.testmail.top/
435436
DISPOSABLE_EMAIL_TOKEN = os.environ.get('DISPOSABLE_EMAIL_TOKEN', None)
437+
438+
# Messages services config
439+
MESSAGES_SERVICES = {
440+
'SlackMessageService': {
441+
'ACCESS_TOKEN': os.environ.get('SLACK_ACCESS_TOKEN', None),
442+
'ANNOUNCEMENT_CHANNEL': os.environ.get('SLACK_ANNOUNCEMENT_CHANNEL', '#announcements')
443+
}
444+
}
445+
446+
if len(MESSAGES_SERVICES) > 0:
447+
CRONJOBS.append(('*/5 * * * *', 'django.core.management.call_command', ['send_announcements']))
448+
449+
# DateTime formats
450+
USE_L10N = False
451+
DATETIME_FORMAT = 'N j, Y, H:i'
452+
SHORT_DATETIME_FORMAT = 'Y/m/d H:i'
453+
TIME_FORMAT = 'H:i:s'
454+
SHORT_DATE_FORMAT = 'Y/m/d'

app/static/css/theme.scss

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,27 @@ body.dark {
131131
.c3-tooltip tr {
132132
color: #222222;
133133
}
134+
.tempus-dominus-widget {
135+
background: lighten($dark, 5%) !important;
136+
color: $white !important;
137+
.date-container-days div:not(.no-highlight).active, .date-container-decades div:not(.no-highlight).active, .date-container-months div:not(.no-highlight).active, .date-container-years div:not(.no-highlight).active, .time-container-clock div:not(.no-highlight).active, .time-container-hour div:not(.no-highlight).active, .time-container-minute div:not(.no-highlight).active, .time-container-second div:not(.no-highlight).active {
138+
color: $white !important;
139+
background: $primary !important;
140+
}
141+
}
142+
}
143+
.light {
144+
.tempus-dominus-widget {
145+
background: darken($light, 5%) !important;
146+
color: $black !important;
147+
.date-container-days div:not(.no-highlight).active, .date-container-decades div:not(.no-highlight).active, .date-container-months div:not(.no-highlight).active, .date-container-years div:not(.no-highlight).active, .time-container-clock div:not(.no-highlight).active, .time-container-hour div:not(.no-highlight).active, .time-container-minute div:not(.no-highlight).active, .time-container-second div:not(.no-highlight).active {
148+
color: $white !important;
149+
background: $primary !important;
150+
}
151+
}
134152
}
135153

154+
136155
::-moz-selection {
137156
color: $white;
138157
background: $primary;

app/template.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ def get_main_nav(request):
3636
nav.append(('Landing page', getattr(settings, 'HACKATHON_LANDING')))
3737
if request.user.has_module_perms('event'):
3838
nav.append(('Checkin', reverse('checkin_list')))
39+
if is_installed('event.messages') and request.user.has_perm('event_messages.view_announcement'):
40+
nav.append(('Announcements', reverse('announcement_list')))
3941
if is_installed('event.meals') and request.user.has_perm('meals.can_checkin_meals'):
4042
nav.append(('Meals', reverse('meals_list')))
4143
if request.user.is_organizer():

app/templates/components/bootstrap5_form.html

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,105 @@
7373
})
7474
</script>
7575
{% endif %}
76+
{% if field.datetimepicker %}
77+
<script>
78+
$(document).ready(() => {
79+
let field = document.getElementById('{{ field.field.auto_id }}')
80+
let options = {
81+
...(TempusDominusSettings ?? {}),
82+
}
83+
{% if field.datetimepicker.type == "TimeField" %}
84+
options.display.components = {
85+
calendar: false,
86+
date: false,
87+
month: false,
88+
year: false,
89+
decades: false,
90+
clock: true,
91+
hours: true,
92+
minutes: true,
93+
seconds: true,
94+
}
95+
options.localization.format = 'HH:mm:ss'
96+
{% elif field.datetimepicker.type == "DateField" %}
97+
options.display.components = {
98+
calendar: true,
99+
date: true,
100+
month: true,
101+
year: true,
102+
decades: true,
103+
clock: false,
104+
hours: false,
105+
minutes: false,
106+
seconds: false,
107+
}
108+
options.localization.format = 'yyyy-MM-dd'
109+
{% elif field.datetimepicker.type == "DateTimeField" %}
110+
options.display.components = {
111+
calendar: true,
112+
date: true,
113+
month: true,
114+
year: true,
115+
decades: true,
116+
clock: true,
117+
hours: true,
118+
minutes: true,
119+
seconds: false,
120+
}
121+
options.localization.format = 'yyyy-MM-dd HH:mm:ss'
122+
{% endif %}
123+
{% if field.datetimepicker.minDate %}
124+
options.restrictions.minDate = Date('{{ field.datetimepicker.minDate }}');
125+
{% endif %}
126+
{% if field.datetimepicker.maxDate %}
127+
options.restrictions.maxDate = Date('{{ field.datetimepicker.maxDate }}');
128+
{% endif %}
129+
{% if field.datetimepicker.disabledDates %}
130+
let disabledDates = []
131+
{% for date in field.datetimepicker.disabledDates %}
132+
disabledDates.push(Date('{{ date }}'))
133+
{% endfor %}
134+
options.restrictions.disabledDates = disabledDates
135+
{% endif %}
136+
{% if field.datetimepicker.enabledDates %}
137+
let enabledDates = []
138+
{% for date in field.datetimepicker.enabledDates %}
139+
enabledDates.push(Date('{{ date }}'))
140+
{% endfor %}
141+
options.restrictions.enabledDates = enabledDates
142+
{% endif %}
143+
{% if field.datetimepicker.daysOfWeekDisabled %}
144+
let daysOfWeekDisabled = []
145+
{% for day in field.datetimepicker.daysOfWeekDisabled %}
146+
daysOfWeekDisabled.push('{{ day }}')
147+
{% endfor %}
148+
options.restrictions.daysOfWeekDisabled = daysOfWeekDisabled
149+
{% endif %}
150+
{% if field.datetimepicker.disabledTimeIntervals %}
151+
let disabledTimeIntervals = []
152+
{% for interval in field.datetimepicker.disabledTimeIntervals %}
153+
disabledTimeIntervals.push(parseInt('{{ interval }}'))
154+
{% endfor %}
155+
options.restrictions.disabledTimeIntervals = disabledTimeIntervals
156+
{% endif %}
157+
{% if field.datetimepicker.disabledHours %}
158+
let disabledHours = []
159+
{% for hour in field.datetimepicker.disabledHours %}
160+
disabledHours.push('{{ hour }}')
161+
{% endfor %}
162+
options.restrictions.disabledHours = disabledHours
163+
{% endif %}
164+
{% if field.datetimepicker.enabledHours %}
165+
let enabledHours = []
166+
{% for hour in field.datetimepicker.enabledHours %}
167+
disabledHours.push('{{ hour }}')
168+
{% endfor %}
169+
options.restrictions.enabledHours = enabledHours
170+
{% endif %}
171+
new tempusDominus.TempusDominus(field, options)
172+
})
173+
</script>
174+
{% endif %}
76175
{% endfor %}
77176
</div>
78177
<script nonce="{{ CSP_NONCE }}">

app/templates/components/imports.html

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
{% compress css %}
66
<link type="text/x-scss" href="{% static 'css/theme.scss' %}" rel="stylesheet" media="screen">
77
{% endcompress %}
8-
{#<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"#}
9-
{# integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">#}
108
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"
119
integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p"
1210
crossorigin="anonymous"></script>
@@ -26,6 +24,15 @@
2624
<!-- TypeHead -->
2725
<script src="{% static 'lib/typeahead.bundle.js' %}"></script>
2826

27+
<!-- Popperjs -->
28+
<script src="https://cdn.jsdelivr.net/npm/@popperjs/[email protected]/dist/umd/popper.min.js" crossorigin="anonymous"></script>
29+
30+
<!-- Tempus Dominus -->
31+
<script src="https://cdn.jsdelivr.net/npm/@eonasdan/[email protected]/dist/js/tempus-dominus.min.js" crossorigin="anonymous"></script>
32+
<script src="https://cdn.jsdelivr.net/npm/@eonasdan/[email protected]/dist/plugins/customDateFormat.js" crossorigin="anonymous"></script>
33+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@eonasdan/[email protected]/dist/css/tempus-dominus.min.css" crossorigin="anonymous">
34+
{% include 'components/tempus_dominus_settings.html' %}
35+
2936
<!-- Our WebPage style -->
3037
<link href="{% static 'css/main.css' %}" rel="stylesheet">
3138

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
{% load i18n %}
2+
<script>
3+
tempusDominus.extend(tempusDominus.plugins.customDateFormat);
4+
const TempusDominusSettings = {
5+
restrictions: {
6+
minDate: undefined,
7+
maxDate: undefined,
8+
disabledDates: [],
9+
enabledDates: [],
10+
daysOfWeekDisabled: [],
11+
disabledTimeIntervals: [],
12+
disabledHours: [],
13+
enabledHours: []
14+
},
15+
display: {
16+
icons: {
17+
type: 'icons',
18+
time: 'fa-solid fa-clock',
19+
date: 'fa-solid fa-calendar',
20+
up: 'fa-solid fa-arrow-up',
21+
down: 'fa-solid fa-arrow-down',
22+
previous: 'fa-solid fa-chevron-left',
23+
next: 'fa-solid fa-chevron-right',
24+
today: 'fa-solid fa-calendar-check',
25+
clear: 'fa-solid fa-trash',
26+
close: 'fa-solid fa-xmark'
27+
},
28+
sideBySide: false,
29+
calendarWeeks: false,
30+
viewMode: 'calendar',
31+
toolbarPlacement: 'bottom',
32+
keepOpen: false,
33+
buttons: {
34+
today: true,
35+
clear: false,
36+
close: false
37+
},
38+
inline: false,
39+
theme: '{{ theme }}'
40+
},
41+
localization: {
42+
today: '{% trans 'Go to today' %}',
43+
clear: '{% trans 'Clear selection' %}',
44+
close: '{% trans 'Close the picker' %}',
45+
selectMonth: '{% trans 'Select Month' %}',
46+
previousMonth: '{% trans 'Previous Month' %}',
47+
nextMonth: '{% trans 'Next Month' %}',
48+
selectYear: '{% trans 'Select Year' %}',
49+
previousYear: '{% trans 'Previous Year' %}',
50+
nextYear: '{% trans 'Next Year' %}',
51+
selectDecade: '{% trans 'Select Decade' %}',
52+
previousDecade: '{% trans 'Previous Decade' %}',
53+
nextDecade: '{% trans 'Next Decade' %}',
54+
previousCentury: '{% trans 'Previous Century' %}',
55+
nextCentury: '{% trans 'Next Century' %}',
56+
pickHour: '{% trans 'Pick Hour' %}',
57+
incrementHour: '{% trans 'Increment Hour' %}',
58+
decrementHour: '{% trans 'Decrement Hour' %}',
59+
pickMinute: '{% trans 'Pick Minute' %}',
60+
incrementMinute: '{% trans 'Increment Minute' %}',
61+
decrementMinute: '{% trans 'Decrement Minute' %}',
62+
pickSecond: '{% trans 'Pick Second' %}',
63+
incrementSecond: '{% trans 'Increment Second' %}',
64+
decrementSecond: '{% trans 'Decrement Second' %}',
65+
toggleMeridiem: '{% trans 'Toggle Meridiem' %}',
66+
selectTime: '{% trans 'Select Time' %}',
67+
selectDate: '{% trans 'Select Date' %}',
68+
dayViewHeaderFormat: { month: 'long', year: '2-digit' },
69+
locale: 'default',
70+
startOfTheWeek: 1,
71+
hourCycle: 'h23',
72+
/**
73+
* This is only used with the customDateFormat plugin
74+
*/
75+
format: 'yyyy-MM-dd HH:mm:ss'
76+
},
77+
keepInvalid: false,
78+
debug: false,
79+
allowInputToggle: true,
80+
multipleDates: false,
81+
multipleDatesSeparator: '; ',
82+
promptTimeOnDateChange: true,
83+
promptTimeOnDateChangeTransitionDelay: 200,
84+
meta: {},
85+
container: undefined
86+
}
87+
</script>

app/utils.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,10 @@ def is_installed(app):
3232

3333
def is_instance_on_db(instance):
3434
return instance is not None and instance._state.db is not None and not instance._state.adding
35+
36+
37+
def notify_user(message, user):
38+
if is_installed('event.messages'):
39+
from event.messages.services import MessageServiceManager
40+
return MessageServiceManager().send_message_to_user(message, user)
41+
return False

event/messages/__init__.py

Whitespace-only changes.

event/messages/admin.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from django import forms
2+
from django.contrib import admin
3+
4+
from event.messages.forms import AnnouncementModelFormMixin
5+
from event.messages.models import Announcement
6+
7+
8+
class AnnouncementAdminForm(AnnouncementModelFormMixin, forms.ModelForm):
9+
class Meta:
10+
model = Announcement
11+
fields = '__all__'
12+
13+
14+
class AnnouncementAdmin(admin.ModelAdmin):
15+
list_filter = ('status', 'services')
16+
search_fields = ('name', 'message')
17+
form = AnnouncementAdminForm
18+
19+
20+
admin.site.register(Announcement, AnnouncementAdmin)

0 commit comments

Comments
 (0)