Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
thalia
concrexit
Commits
b83242cc
Commit
b83242cc
authored
Feb 03, 2017
by
Thom Wiggers
📐
Browse files
Merge branch '250-build-an-announcement-feature' into 'master'
Create announcements Closes
#250
See merge request
!338
parents
4bcad194
dfac69f1
Changes
16
Hide whitespace changes
Inline
Side-by-side
website/announcements/__init__.py
0 → 100644
View file @
b83242cc
website/announcements/admin.py
0 → 100644
View file @
b83242cc
from
django.contrib
import
admin
from
utils.translation
import
TranslatedModelAdmin
from
.models
import
Announcement
@
admin
.
register
(
Announcement
)
class
AnnouncementAdmin
(
TranslatedModelAdmin
):
list_display
=
(
'content'
,
'since'
,
'until'
,
'visible'
)
def
visible
(
self
,
obj
):
return
obj
.
is_visible
visible
.
boolean
=
True
website/announcements/apps.py
0 → 100644
View file @
b83242cc
from
django.apps
import
AppConfig
class
AnnouncementsConfig
(
AppConfig
):
name
=
'announcements'
website/announcements/context_processors.py
0 → 100644
View file @
b83242cc
from
.models
import
Announcement
def
announcements
(
request
):
closed_announcements
=
request
.
session
.
get
(
'closed_announcements'
,
[])
announcements
=
[
a
for
a
in
Announcement
.
objects
.
all
()
if
a
.
is_visible
and
a
.
pk
not
in
closed_announcements
]
return
{
'announcements'
:
announcements
}
website/announcements/migrations/0001_initial.py
0 → 100644
View file @
b83242cc
# -*- coding: utf-8 -*-
# Generated by Django 1.10.5 on 2017-01-30 20:47
from
__future__
import
unicode_literals
from
django.db
import
migrations
,
models
import
django.utils.timezone
class
Migration
(
migrations
.
Migration
):
initial
=
True
dependencies
=
[
]
operations
=
[
migrations
.
CreateModel
(
name
=
'Announcement'
,
fields
=
[
(
'id'
,
models
.
AutoField
(
auto_created
=
True
,
primary_key
=
True
,
serialize
=
False
,
verbose_name
=
'ID'
)),
(
'since'
,
models
.
DateField
(
default
=
django
.
utils
.
timezone
.
now
,
help_text
=
'Hide this announcement before this time.'
,
verbose_name
=
'Display since'
)),
(
'until'
,
models
.
DateField
(
blank
=
True
,
help_text
=
'Hide this announcement after this time.'
,
null
=
True
,
verbose_name
=
'Display until'
)),
(
'icon'
,
models
.
CharField
(
default
=
'bullhorn'
,
help_text
=
'Font Awesome abbreviation for icon to use.'
,
max_length
=
150
,
verbose_name
=
'Font Awesome icon'
)),
(
'closeable'
,
models
.
BooleanField
(
default
=
True
)),
(
'content_en'
,
models
.
CharField
(
help_text
=
'The content of the announcement; what text to display.'
,
max_length
=
500
,
verbose_name
=
'Content (EN)'
)),
(
'content_nl'
,
models
.
CharField
(
help_text
=
'The content of the announcement; what text to display.'
,
max_length
=
500
,
verbose_name
=
'Content (NL)'
)),
],
options
=
{
'ordering'
:
(
'-since'
,),
},
),
]
website/announcements/migrations/__init__.py
0 → 100644
View file @
b83242cc
website/announcements/models.py
0 → 100644
View file @
b83242cc
from
django.db
import
models
from
django.utils.translation
import
ugettext_lazy
as
_
from
django.utils
import
timezone
from
utils.translation
import
ModelTranslateMeta
,
MultilingualField
class
Announcement
(
models
.
Model
,
metaclass
=
ModelTranslateMeta
):
content
=
MultilingualField
(
models
.
CharField
,
verbose_name
=
_
(
'Content'
),
help_text
=
_
(
'The content of the announcement; what text to display.'
),
blank
=
False
,
max_length
=
500
,
)
since
=
models
.
DateField
(
verbose_name
=
_
(
'Display since'
),
help_text
=
_
(
"Hide this announcement before this time."
),
default
=
timezone
.
now
,
)
until
=
models
.
DateField
(
verbose_name
=
_
(
'Display until'
),
help_text
=
_
(
"Hide this announcement after this time."
),
blank
=
True
,
null
=
True
,
)
icon
=
models
.
CharField
(
verbose_name
=
_
(
'Font Awesome icon'
),
help_text
=
_
(
"Font Awesome abbreviation for icon to use."
),
max_length
=
150
,
default
=
'bullhorn'
,
)
closeable
=
models
.
BooleanField
(
default
=
True
)
class
Meta
:
ordering
=
(
'-since'
,
)
def
__str__
(
self
):
return
self
.
content
@
property
def
is_visible
(
self
):
"""Is this announcement currently visible"""
return
((
self
.
until
is
None
or
self
.
until
>
timezone
.
now
().
date
())
and
(
self
.
since
is
None
or
self
.
since
<=
timezone
.
now
().
date
()))
website/announcements/static/announcements/css/style.scss
0 → 100644
View file @
b83242cc
.announcement
{
background
:
#E62272
;
color
:
#FFFFFF
;
font-weight
:
bold
;
text-align
:center
;
padding
:
10px
;
position
:
relative
;
.close
{
color
:
#FFFFFF
;
}
}
website/announcements/static/announcements/js/announcements.js
0 → 100644
View file @
b83242cc
(
function
()
{
$
(
'
.announcement .close
'
).
click
(
function
()
{
$
(
this
).
parent
().
remove
();
$
.
ajax
({
// make sure this matches the url defined in announcements/urls.py
url
:
"
/announcements/close-announcement
"
,
type
:
"
POST
"
,
beforeSend
:
function
(
xhr
){
xhr
.
setRequestHeader
(
"
X-CSRFToken
"
,
Cookies
.
get
(
'
csrftoken
'
));
},
data
:
{
id
:
$
(
this
).
data
(
'
announcement-id
'
)},
})
});
})();
website/announcements/templates/announcements/announcement.html
0 → 100644
View file @
b83242cc
{% for announcement in announcements %}
<div
class=
'announcement'
>
<i
class=
"fa fa-{{ announcement.icon }}"
></i>
{{ announcement.content }}
{% if announcement.closeable %}
<button
type=
"button"
class=
"close"
aria-label=
"Close"
data-announcement-id=
"{{ announcement.pk }}"
>
<span
aria-hidden=
"true"
>
×
</span>
</button>
{% endif %}
</div>
{% endfor %}
website/announcements/urls.py
0 → 100644
View file @
b83242cc
from
django.conf.urls
import
url
from
.
import
views
app_name
=
"announcements"
urlpatterns
=
[
url
(
r
'^close-announcement$'
,
views
.
close_announcement
,
name
=
'close-announcement'
)
]
website/announcements/views.py
0 → 100644
View file @
b83242cc
from
django.http
import
HttpResponse
def
close_announcement
(
request
):
id
=
int
(
request
.
POST
[
'id'
])
# if we do not have a list of closed announcements yet:
if
'closed_announcements'
not
in
request
.
session
:
request
.
session
[
'closed_announcements'
]
=
[]
# cannot use sets here :(
# duplicates should never occur anyway, but it does not hurt to check
if
id
not
in
request
.
session
[
'closed_announcements'
]:
request
.
session
[
'closed_announcements'
].
append
(
id
)
# needs to be explicitly marked since we only edited an existing object
request
.
session
.
modified
=
True
return
HttpResponse
(
status
=
204
)
# 204: No Content
website/thaliawebsite/settings/settings.py
View file @
b83242cc
...
...
@@ -70,6 +70,7 @@ INSTALLED_APPS = [
'newsletters'
,
'education'
,
'thaliapp'
,
'announcements'
,
]
MIDDLEWARE
=
[
...
...
@@ -102,6 +103,7 @@ TEMPLATES = [
'django.contrib.auth.context_processors.auth'
,
'django.contrib.messages.context_processors.messages'
,
'partners.context_processors.showcased_partners'
,
'announcements.context_processors.announcements'
,
],
},
},
...
...
website/thaliawebsite/static/plugins/js.cookie-2.1.3.min.js
0 → 100644
View file @
b83242cc
/*! js-cookie v2.1.3 | MIT */
!
function
(
a
){
var
b
=!
1
;
if
(
"
function
"
==
typeof
define
&&
define
.
amd
&&
(
define
(
a
),
b
=!
0
),
"
object
"
==
typeof
exports
&&
(
module
.
exports
=
a
(),
b
=!
0
),
!
b
){
var
c
=
window
.
Cookies
,
d
=
window
.
Cookies
=
a
();
d
.
noConflict
=
function
(){
return
window
.
Cookies
=
c
,
d
}}}(
function
(){
function
a
(){
for
(
var
a
=
0
,
b
=
{};
a
<
arguments
.
length
;
a
++
){
var
c
=
arguments
[
a
];
for
(
var
d
in
c
)
b
[
d
]
=
c
[
d
]}
return
b
}
function
b
(
c
){
function
d
(
b
,
e
,
f
){
var
g
;
if
(
"
undefined
"
!=
typeof
document
){
if
(
arguments
.
length
>
1
){
if
(
f
=
a
({
path
:
"
/
"
},
d
.
defaults
,
f
),
"
number
"
==
typeof
f
.
expires
){
var
h
=
new
Date
;
h
.
setMilliseconds
(
h
.
getMilliseconds
()
+
864
e5
*
f
.
expires
),
f
.
expires
=
h
}
try
{
g
=
JSON
.
stringify
(
e
),
/^
[\{\[]
/
.
test
(
g
)
&&
(
e
=
g
)}
catch
(
i
){}
return
e
=
c
.
write
?
c
.
write
(
e
,
b
):
encodeURIComponent
(
e
+
""
).
replace
(
/%
(
23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C
)
/g
,
decodeURIComponent
),
b
=
encodeURIComponent
(
b
+
""
),
b
=
b
.
replace
(
/%
(
23|24|26|2B|5E|60|7C
)
/g
,
decodeURIComponent
),
b
=
b
.
replace
(
/
[\(\)]
/g
,
escape
),
document
.
cookie
=
b
+
"
=
"
+
e
+
(
f
.
expires
?
"
; expires=
"
+
f
.
expires
.
toUTCString
():
""
)
+
(
f
.
path
?
"
; path=
"
+
f
.
path
:
""
)
+
(
f
.
domain
?
"
; domain=
"
+
f
.
domain
:
""
)
+
(
f
.
secure
?
"
; secure
"
:
""
)}
b
||
(
g
=
{});
for
(
var
j
=
document
.
cookie
?
document
.
cookie
.
split
(
"
;
"
):[],
k
=
/
(
%
[
0-9A-Z
]{2})
+/g
,
l
=
0
;
l
<
j
.
length
;
l
++
){
var
m
=
j
[
l
].
split
(
"
=
"
),
n
=
m
.
slice
(
1
).
join
(
"
=
"
);
'
"
'
===
n
.
charAt
(
0
)
&&
(
n
=
n
.
slice
(
1
,
-
1
));
try
{
var
o
=
m
[
0
].
replace
(
k
,
decodeURIComponent
);
if
(
n
=
c
.
read
?
c
.
read
(
n
,
o
):
c
(
n
,
o
)
||
n
.
replace
(
k
,
decodeURIComponent
),
this
.
json
)
try
{
n
=
JSON
.
parse
(
n
)}
catch
(
i
){}
if
(
b
===
o
){
g
=
n
;
break
}
b
||
(
g
[
o
]
=
n
)}
catch
(
i
){}}
return
g
}}
return
d
.
set
=
d
,
d
.
get
=
function
(
a
){
return
d
.
call
(
d
,
a
)},
d
.
getJSON
=
function
(){
return
d
.
apply
({
json
:
!
0
},[].
slice
.
call
(
arguments
))},
d
.
defaults
=
{},
d
.
remove
=
function
(
b
,
c
){
d
(
b
,
""
,
a
(
c
,{
expires
:
-
1
}))},
d
.
withConverter
=
b
,
d
}
return
b
(
function
(){})});
\ No newline at end of file
website/thaliawebsite/templates/base.html
View file @
b83242cc
...
...
@@ -35,6 +35,7 @@
<link
href=
"{% static "
css
/
jquery.fancybox.css
"
%}"
rel=
"stylesheet"
type=
"text/css"
>
<link
href=
"{% static "
css
/
style.scss
"
%}"
rel=
"stylesheet"
type=
"text/x-scss"
>
<link
href=
"{% static "
announcements
/
css
/
style.scss
"
%}"
rel=
"stylesheet"
type=
"text/x-scss"
>
{% endcompress %}{% endblock %}
<!-- js head -->
...
...
@@ -55,6 +56,9 @@
<!-- #wrapper -->
<div
class=
"thimbus-page"
>
<!-- #announcements -->
{% include "announcements/announcement.html" with announcements=announcements %}
<!-- #header -->
<div
id=
"header"
class=
"wrapper"
>
...
...
@@ -191,11 +195,13 @@
<script
type=
"text/javascript"
src=
"{% static "
plugins
/
jquery.easing.1.3.js
"
%}"
></script>
<script
type=
"text/javascript"
src=
"{% static "
plugins
/
jquery.isotope.min.js
"
%}"
></script>
<script
type=
"text/javascript"
src=
"{% static "
plugins
/
magnific-popup
/
jquery.magnific-popup.min.js
"
%}"
></script>
<script
type=
"text/javascript"
src=
"{% static "
plugins
/
js.cookie-2.1.3.min.js
"
%}"
></script>
{% endblock %}
{% block js_footer %}
<script
type=
"text/javascript"
src=
"{% static "
js
/
footer.js
"
%}"
></script>
<script
type=
"text/javascript"
src=
"{% static "
js
/
bootstrap-dropdown.js
"
%}"
></script>
<script
type=
"text/javascript"
src=
"{% static "
announcements
/
js
/
announcements.js
"
%}"
></script>
{% endblock %}
</body>
<!-- /body -->
...
...
website/thaliawebsite/urls.py
View file @
b83242cc
...
...
@@ -94,6 +94,7 @@ urlpatterns = [
url
(
r
'wikilogin'
,
views
.
wiki_login
),
])),
url
(
r
'^education/'
,
include
(
'education.urls'
)),
url
(
r
'^announcements/'
,
include
(
'announcements.urls'
)),
# Default login helpers
url
(
r
'^'
,
include
(
'django.contrib.auth.urls'
)),
url
(
r
'^i18n/'
,
include
(
'django.conf.urls.i18n'
)),
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment