Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
CIRCLE
/
cloud
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
94
Merge Requests
10
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
c10c1a49
authored
Apr 24, 2015
by
Bach Dániel
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
dashboard: add broadcast message support
parent
ff91b0ff
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
291 additions
and
3 deletions
+291
-3
circle/circle/settings/base.py
+1
-0
circle/dashboard/admin.py
+3
-1
circle/dashboard/context_processors.py
+11
-0
circle/dashboard/forms.py
+13
-1
circle/dashboard/migrations/0003_message.py
+33
-0
circle/dashboard/models.py
+25
-0
circle/dashboard/static/dashboard/dashboard.js
+19
-0
circle/dashboard/static/dashboard/dashboard.less
+6
-0
circle/dashboard/tables.py
+16
-1
circle/dashboard/templates/base.html
+17
-0
circle/dashboard/templates/dashboard/base.html
+6
-0
circle/dashboard/templates/dashboard/message-create.html
+27
-0
circle/dashboard/templates/dashboard/message-edit.html
+34
-0
circle/dashboard/templates/dashboard/message-list.html
+28
-0
circle/dashboard/urls.py
+10
-0
circle/dashboard/views/__init__.py
+1
-0
circle/dashboard/views/message.py
+41
-0
No files found.
circle/circle/settings/base.py
View file @
c10c1a49
...
@@ -282,6 +282,7 @@ TEMPLATE_CONTEXT_PROCESSORS = (
...
@@ -282,6 +282,7 @@ TEMPLATE_CONTEXT_PROCESSORS = (
'django.core.context_processors.request'
,
'django.core.context_processors.request'
,
'dashboard.context_processors.notifications'
,
'dashboard.context_processors.notifications'
,
'dashboard.context_processors.extract_settings'
,
'dashboard.context_processors.extract_settings'
,
'dashboard.context_processors.broadcast_messages'
,
)
)
# See: https://docs.djangoproject.com/en/dev/ref/settings/#template-dirs
# See: https://docs.djangoproject.com/en/dev/ref/settings/#template-dirs
...
...
circle/dashboard/admin.py
View file @
c10c1a49
...
@@ -21,7 +21,7 @@ from django import contrib
...
@@ -21,7 +21,7 @@ from django import contrib
from
django.contrib.auth.admin
import
UserAdmin
,
GroupAdmin
from
django.contrib.auth.admin
import
UserAdmin
,
GroupAdmin
from
django.contrib.auth.models
import
User
,
Group
from
django.contrib.auth.models
import
User
,
Group
from
dashboard.models
import
Profile
,
GroupProfile
,
ConnectCommand
from
dashboard.models
import
Profile
,
GroupProfile
,
ConnectCommand
,
Message
class
ProfileInline
(
contrib
.
admin
.
TabularInline
):
class
ProfileInline
(
contrib
.
admin
.
TabularInline
):
...
@@ -43,3 +43,5 @@ contrib.admin.site.unregister(User)
...
@@ -43,3 +43,5 @@ contrib.admin.site.unregister(User)
contrib
.
admin
.
site
.
register
(
User
,
UserAdmin
)
contrib
.
admin
.
site
.
register
(
User
,
UserAdmin
)
contrib
.
admin
.
site
.
unregister
(
Group
)
contrib
.
admin
.
site
.
unregister
(
Group
)
contrib
.
admin
.
site
.
register
(
Group
,
GroupAdmin
)
contrib
.
admin
.
site
.
register
(
Group
,
GroupAdmin
)
contrib
.
admin
.
site
.
register
(
Message
)
circle/dashboard/context_processors.py
View file @
c10c1a49
...
@@ -16,6 +16,10 @@
...
@@ -16,6 +16,10 @@
# with CIRCLE. If not, see <http://www.gnu.org/licenses/>.
# with CIRCLE. If not, see <http://www.gnu.org/licenses/>.
from
django.conf
import
settings
from
django.conf
import
settings
from
django.db.models
import
Q
from
django.utils
import
timezone
from
.models
import
Message
def
notifications
(
request
):
def
notifications
(
request
):
...
@@ -31,3 +35,10 @@ def extract_settings(request):
...
@@ -31,3 +35,10 @@ def extract_settings(request):
'COMPANY_NAME'
:
getattr
(
settings
,
"COMPANY_NAME"
,
None
),
'COMPANY_NAME'
:
getattr
(
settings
,
"COMPANY_NAME"
,
None
),
'ADMIN_ENABLED'
:
getattr
(
settings
,
"ADMIN_ENABLED"
,
False
),
'ADMIN_ENABLED'
:
getattr
(
settings
,
"ADMIN_ENABLED"
,
False
),
}
}
def
broadcast_messages
(
request
):
now
=
timezone
.
now
()
messages
=
Message
.
objects
.
filter
(
enabled
=
True
)
.
exclude
(
Q
(
starts_at__gt
=
now
)
|
Q
(
ends_at__lt
=
now
))
return
{
'broadcast_messages'
:
messages
}
circle/dashboard/forms.py
View file @
c10c1a49
...
@@ -57,7 +57,7 @@ from vm.models import (
...
@@ -57,7 +57,7 @@ from vm.models import (
from
storage.models
import
DataStore
,
Disk
from
storage.models
import
DataStore
,
Disk
from
django.contrib.admin.widgets
import
FilteredSelectMultiple
from
django.contrib.admin.widgets
import
FilteredSelectMultiple
from
django.contrib.auth.models
import
Permission
from
django.contrib.auth.models
import
Permission
from
.models
import
Profile
,
GroupProfile
from
.models
import
Profile
,
GroupProfile
,
Message
from
circle.settings.base
import
LANGUAGES
,
MAX_NODE_RAM
from
circle.settings.base
import
LANGUAGES
,
MAX_NODE_RAM
from
django.utils.translation
import
string_concat
from
django.utils.translation
import
string_concat
...
@@ -1624,3 +1624,15 @@ class DiskForm(ModelForm):
...
@@ -1624,3 +1624,15 @@ class DiskForm(ModelForm):
model
=
Disk
model
=
Disk
fields
=
(
"name"
,
"filename"
,
"datastore"
,
"type"
,
"bus"
,
"size"
,
fields
=
(
"name"
,
"filename"
,
"datastore"
,
"type"
,
"bus"
,
"size"
,
"base"
,
"dev_num"
,
"destroyed"
,
"is_ready"
,
)
"base"
,
"dev_num"
,
"destroyed"
,
"is_ready"
,
)
class
MessageForm
(
ModelForm
):
class
Meta
:
model
=
Message
fields
=
(
"message"
,
"enabled"
,
"effect"
,
"starts_at"
,
"ends_at"
)
@property
def
helper
(
self
):
helper
=
FormHelper
()
helper
.
add_input
(
Submit
(
"submit"
,
_
(
"Save"
)))
return
helper
circle/dashboard/migrations/0003_message.py
0 → 100644
View file @
c10c1a49
# -*- coding: utf-8 -*-
from
__future__
import
unicode_literals
from
django.db
import
models
,
migrations
import
django.utils.timezone
import
model_utils.fields
class
Migration
(
migrations
.
Migration
):
dependencies
=
[
(
'dashboard'
,
'0002_auto_20150318_1317'
),
]
operations
=
[
migrations
.
CreateModel
(
name
=
'Message'
,
fields
=
[
(
'id'
,
models
.
AutoField
(
verbose_name
=
'ID'
,
serialize
=
False
,
auto_created
=
True
,
primary_key
=
True
)),
(
'created'
,
model_utils
.
fields
.
AutoCreatedField
(
default
=
django
.
utils
.
timezone
.
now
,
verbose_name
=
'created'
,
editable
=
False
)),
(
'modified'
,
model_utils
.
fields
.
AutoLastModifiedField
(
default
=
django
.
utils
.
timezone
.
now
,
verbose_name
=
'modified'
,
editable
=
False
)),
(
'message'
,
models
.
CharField
(
max_length
=
500
,
verbose_name
=
'message'
)),
(
'starts_at'
,
models
.
DateTimeField
(
null
=
True
,
verbose_name
=
'starts at'
,
blank
=
True
)),
(
'ends_at'
,
models
.
DateTimeField
(
null
=
True
,
verbose_name
=
'ends at'
,
blank
=
True
)),
(
'effect'
,
models
.
CharField
(
default
=
b
'info'
,
max_length
=
10
,
verbose_name
=
'effect'
,
choices
=
[(
b
'success'
,
'success'
),
(
b
'info'
,
'info'
),
(
b
'warning'
,
'warning'
),
(
b
'danger'
,
'danger'
)])),
(
'enabled'
,
models
.
BooleanField
(
default
=
False
,
verbose_name
=
'enabled'
)),
],
options
=
{
'ordering'
:
[
'-ends_at'
],
},
bases
=
(
models
.
Model
,),
),
]
circle/dashboard/models.py
View file @
c10c1a49
...
@@ -59,6 +59,31 @@ def pwgen():
...
@@ -59,6 +59,31 @@ def pwgen():
return
User
.
objects
.
make_random_password
()
return
User
.
objects
.
make_random_password
()
class
Message
(
TimeStampedModel
):
message
=
CharField
(
max_length
=
500
,
verbose_name
=
_
(
'message'
))
starts_at
=
DateTimeField
(
null
=
True
,
blank
=
True
,
verbose_name
=
_
(
'starts at'
))
ends_at
=
DateTimeField
(
null
=
True
,
blank
=
True
,
verbose_name
=
_
(
'ends at'
))
effect
=
CharField
(
default
=
'info'
,
max_length
=
10
,
verbose_name
=
_
(
'effect'
),
choices
=
((
'success'
,
_
(
'success'
)),
(
'info'
,
_
(
'info'
)),
(
'warning'
,
_
(
'warning'
)),
(
'danger'
,
_
(
'danger'
))))
enabled
=
BooleanField
(
default
=
False
,
verbose_name
=
_
(
'enabled'
))
class
Meta
:
ordering
=
[
"id"
]
verbose_name
=
_
(
'message'
)
verbose_name_plural
=
_
(
'messages'
)
def
__unicode__
(
self
):
return
self
.
message
@permalink
def
get_absolute_url
(
self
):
return
(
'dashboard.views.message-detail'
,
None
,
{
'pk'
:
self
.
pk
})
class
Favourite
(
Model
):
class
Favourite
(
Model
):
instance
=
ForeignKey
(
"vm.Instance"
)
instance
=
ForeignKey
(
"vm.Instance"
)
user
=
ForeignKey
(
User
)
user
=
ForeignKey
(
User
)
...
...
circle/dashboard/static/dashboard/dashboard.js
View file @
c10c1a49
...
@@ -527,3 +527,22 @@ function replaceTag(tag) {
...
@@ -527,3 +527,22 @@ function replaceTag(tag) {
function
safe_tags_replace
(
str
)
{
function
safe_tags_replace
(
str
)
{
return
str
.
replace
(
/
[
&<>
]
/g
,
replaceTag
);
return
str
.
replace
(
/
[
&<>
]
/g
,
replaceTag
);
}
}
$
(
function
()
{
var
closed
=
JSON
.
parse
(
getCookie
(
'broadcast-messages'
));
$
(
'.broadcast-message'
).
each
(
function
()
{
var
id
=
$
(
this
).
data
(
'id'
);
if
(
closed
&&
closed
.
indexOf
(
id
)
!=
-
1
)
{
$
(
this
).
remove
()
}
});
$
(
'.broadcast-message'
).
on
(
'closed.bs.alert'
,
function
()
{
var
closed
=
JSON
.
parse
(
getCookie
(
'broadcast-messages'
));
if
(
!
closed
)
{
closed
=
[];
}
closed
.
push
(
$
(
this
).
data
(
'id'
));
setCookie
(
'broadcast-messages'
,
JSON
.
stringify
(
closed
),
7
*
24
*
60
*
60
*
1000
,
"/"
);
});
});
circle/dashboard/static/dashboard/dashboard.less
View file @
c10c1a49
...
@@ -1315,3 +1315,9 @@ textarea[name="new_members"] {
...
@@ -1315,3 +1315,9 @@ textarea[name="new_members"] {
.little-margin-bottom {
.little-margin-bottom {
margin-bottom: 5px;
margin-bottom: 5px;
}
}
.broadcast-message {
margin-bottom: 5px;
padding-top: 5px;
padding-bottom: 5px;
}
circle/dashboard/tables.py
View file @
c10c1a49
...
@@ -29,7 +29,7 @@ from django_sshkey.models import UserKey
...
@@ -29,7 +29,7 @@ from django_sshkey.models import UserKey
from
storage.models
import
Disk
from
storage.models
import
Disk
from
vm.models
import
Node
,
InstanceTemplate
,
Lease
from
vm.models
import
Node
,
InstanceTemplate
,
Lease
from
dashboard.models
import
ConnectCommand
from
dashboard.models
import
ConnectCommand
,
Message
class
FileSizeColumn
(
Column
):
class
FileSizeColumn
(
Column
):
...
@@ -354,3 +354,18 @@ class DiskListTable(Table):
...
@@ -354,3 +354,18 @@ class DiskListTable(Table):
order_by
=
(
"-pk"
,
)
order_by
=
(
"-pk"
,
)
per_page
=
15
per_page
=
15
empty_text
=
_
(
"No disk found."
)
empty_text
=
_
(
"No disk found."
)
class
MessageListTable
(
Table
):
message
=
LinkColumn
(
'dashboard.views.message-detail'
,
args
=
[
A
(
'pk'
)],
attrs
=
{
'th'
:
{
'data-sort'
:
"string"
}}
)
class
Meta
:
model
=
Message
attrs
=
{
'class'
:
"table table-bordered table-striped table-hover"
,
'id'
:
"disk-list-table"
}
order_by
=
(
"-pk"
,
)
fields
=
(
'pk'
,
'message'
,
'enabled'
,
'effect'
)
circle/dashboard/templates/base.html
View file @
c10c1a49
{% load i18n %}
{% load i18n %}
{% load staticfiles %}
{% load staticfiles %}
{% load cache %}
{% load compressed %}
{% load compressed %}
<!DOCTYPE html>
<!DOCTYPE html>
<html
lang=
"{{lang}}"
>
<html
lang=
"{{lang}}"
>
...
@@ -40,6 +41,22 @@
...
@@ -40,6 +41,22 @@
</div>
<!-- navbar navbar-inverse navbar-fixed-top -->
</div>
<!-- navbar navbar-inverse navbar-fixed-top -->
<div
class=
"container"
>
<div
class=
"container"
>
{% block broadcast_messages %}
{% cache 1 broadcast_messages %}
<div
id=
"broadcast-messages"
>
{% for message in broadcast_messages %}
<div
data-id=
{{
message
.
id
}}
class=
"alert alert-{{ message.effect }}
text-center broadcast-message"
>
<button
type=
"button"
class=
"close"
data-dismiss=
"alert"
aria-label=
"Close"
>
<span
aria-hidden=
"true"
>
×
</span>
</button>
{{ message.message|safe }}
</div>
{% endfor %}
</div>
{% endcache %}
{% endblock broadcast_messages %}
{% block messages %}
{% block messages %}
<div
class=
"messagelist"
>
<div
class=
"messagelist"
>
{% if messages %}
{% if messages %}
...
...
circle/dashboard/templates/dashboard/base.html
View file @
c10c1a49
...
@@ -27,6 +27,12 @@
...
@@ -27,6 +27,12 @@
<span
class=
"hidden-sm"
>
{% trans "Admin" %}
</span>
<span
class=
"hidden-sm"
>
{% trans "Admin" %}
</span>
</a>
</a>
</li>
</li>
<li>
<a
href=
"{% url "
dashboard
.
views
.
message-list
"
%}"
>
<i
class=
"fa fa-bullhorn"
></i>
<span
class=
"hidden-sm"
>
{% trans "Messages" %}
</span>
</a>
</li>
{% endif %}
{% endif %}
<li>
<li>
<a
href=
"{% url "
dashboard
.
views
.
storage
"
%}"
>
<a
href=
"{% url "
dashboard
.
views
.
storage
"
%}"
>
...
...
circle/dashboard/templates/dashboard/message-create.html
0 → 100644
View file @
c10c1a49
{% extends "dashboard/base.html" %}
{% load i18n %}
{% load crispy_forms_tags %}
{% block title-page %}{% trans "Broadcast Messages" %}{% endblock %}
{% block content %}
<div
class=
"row"
>
<div
class=
"col-md-12"
>
<div
class=
"panel panel-default"
>
<div
class=
"panel-heading"
>
<a
href=
"{% url "
dashboard
.
views
.
message-list
"
%}"
class=
"btn btn-default btn-xs pull-right"
>
{% trans "Back" %}
</a>
<h3
class=
"no-margin"
>
<i
class=
"fa fa-bullhorn"
></i>
{% trans "New message" %}
</h3>
</div>
<div
class=
"panel-body"
>
{% crispy form %}
</div>
<!-- .panel-body -->
</div>
</div>
</div>
{% endblock %}
circle/dashboard/templates/dashboard/message-edit.html
0 → 100644
View file @
c10c1a49
{% extends "dashboard/base.html" %}
{% load i18n %}
{% load crispy_forms_tags %}
{% block title-page %}{% trans "Broadcast Messages" %}{% endblock %}
{% block content %}
<div
class=
"row"
>
<div
class=
"col-md-12"
>
<div
class=
"panel panel-default"
>
<div
class=
"panel-heading"
>
<div
class=
"pull-right"
>
<a
href=
"{% url "
dashboard
.
views
.
message-list
"
%}"
class=
"btn btn-default btn-xs"
>
{% trans "Back" %}
</a>
<a
href=
"{% url "
dashboard
.
views
.
message-delete
"
pk=
object.pk
%}"
class=
"btn btn-danger btn-xs"
>
{% trans "Delete" %}
</a>
</div>
<h3
class=
"no-margin"
>
<i
class=
"fa fa-bullhorn"
></i>
{% trans "Edit message" %}
</h3>
</div>
<div
class=
"panel-body"
>
{% crispy form %}
</div>
<!-- .panel-body -->
</div>
</div>
</div>
{% endblock %}
circle/dashboard/templates/dashboard/message-list.html
0 → 100644
View file @
c10c1a49
{% extends "dashboard/base.html" %}
{% load staticfiles %}
{% load i18n %}
{% load render_table from django_tables2 %}
{% block title-page %}{% trans "Broadcast Messages" %}{% endblock %}
{% block content %}
<div
class=
"row"
>
<div
class=
"col-md-12"
>
<div
class=
"panel panel-default"
>
<div
class=
"panel-heading"
>
<a
href=
"{% url "
dashboard
.
views
.
message-create
"
%}"
class=
"pull-right btn btn-success btn-xs"
>
<i
class=
"fa fa-plus"
></i>
{% trans "new message" %}
</a>
<h3
class=
"no-margin"
><i
class=
"fa fa-bullhorn"
></i>
{% trans "Broadcast Messages" %}
</h3>
</div>
<div
class=
"panel-body"
>
<div
class=
"table-responsive"
>
{% render_table table %}
</div>
</div>
</div>
</div>
</div>
{% endblock %}
circle/dashboard/urls.py
View file @
c10c1a49
...
@@ -54,6 +54,7 @@ from .views import (
...
@@ -54,6 +54,7 @@ from .views import (
NodeActivityView
,
NodeActivityView
,
UserList
,
UserList
,
StorageDetail
,
DiskDetail
,
StorageDetail
,
DiskDetail
,
MessageList
,
MessageDetail
,
MessageCreate
,
MessageDelete
,
)
)
from
.views.vm
import
vm_ops
,
vm_mass_ops
from
.views.vm
import
vm_ops
,
vm_mass_ops
from
.views.node
import
node_ops
from
.views.node
import
node_ops
...
@@ -232,6 +233,15 @@ urlpatterns = patterns(
...
@@ -232,6 +233,15 @@ urlpatterns = patterns(
name
=
"dashboard.views.storage"
),
name
=
"dashboard.views.storage"
),
url
(
r'^disk/(?P<pk>\d+)/$'
,
DiskDetail
.
as_view
(),
url
(
r'^disk/(?P<pk>\d+)/$'
,
DiskDetail
.
as_view
(),
name
=
"dashboard.views.disk-detail"
),
name
=
"dashboard.views.disk-detail"
),
url
(
r'^message/list/$'
,
MessageList
.
as_view
(),
name
=
"dashboard.views.message-list"
),
url
(
r'^message/(?P<pk>\d+)/$'
,
MessageDetail
.
as_view
(),
name
=
"dashboard.views.message-detail"
),
url
(
r'^message/create/$'
,
MessageCreate
.
as_view
(),
name
=
"dashboard.views.message-create"
),
url
(
r'^message/delete/(?P<pk>\d+)/$'
,
MessageDelete
.
as_view
(),
name
=
"dashboard.views.message-delete"
),
)
)
urlpatterns
+=
patterns
(
urlpatterns
+=
patterns
(
...
...
circle/dashboard/views/__init__.py
View file @
c10c1a49
...
@@ -14,3 +14,4 @@ from vm import *
...
@@ -14,3 +14,4 @@ from vm import *
from
graph
import
*
from
graph
import
*
from
storage
import
*
from
storage
import
*
from
request
import
*
from
request
import
*
from
message
import
*
circle/dashboard/views/message.py
0 → 100644
View file @
c10c1a49
from
django.contrib.messages.views
import
SuccessMessageMixin
from
django.core.urlresolvers
import
reverse
from
django.utils.translation
import
ugettext
as
_
from
django.views.generic
import
CreateView
,
DeleteView
,
UpdateView
from
braces.views
import
SuperuserRequiredMixin
,
LoginRequiredMixin
from
django_tables2
import
SingleTableView
from
..forms
import
MessageForm
from
..models
import
Message
from
..tables
import
MessageListTable
class
MessageList
(
LoginRequiredMixin
,
SuperuserRequiredMixin
,
SingleTableView
):
template_name
=
"dashboard/message-list.html"
model
=
Message
table_class
=
MessageListTable
class
MessageDetail
(
LoginRequiredMixin
,
SuperuserRequiredMixin
,
SuccessMessageMixin
,
UpdateView
):
model
=
Message
template_name
=
"dashboard/message-edit.html"
form_class
=
MessageForm
success_message
=
_
(
"Broadcast message successfully updated."
)
class
MessageCreate
(
LoginRequiredMixin
,
SuperuserRequiredMixin
,
SuccessMessageMixin
,
CreateView
):
model
=
Message
template_name
=
"dashboard/message-create.html"
form_class
=
MessageForm
success_message
=
_
(
"New broadcast message successfully created."
)
class
MessageDelete
(
LoginRequiredMixin
,
SuperuserRequiredMixin
,
DeleteView
):
model
=
Message
template_name
=
"dashboard/confirm/base-delete.html"
def
get_success_url
(
self
):
return
reverse
(
"dashboard.views.message-list"
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a 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