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
1e514d5b
authored
Feb 06, 2015
by
Kálmán Viktor
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'feature-useradmin' into 'master'
Useradmin
✅
Closes
#372
See merge request
!286
parents
8c7786a6
ca4fcef5
Hide whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
470 additions
and
239 deletions
+470
-239
circle/dashboard/forms.py
+49
-2
circle/dashboard/static/dashboard/dashboard.js
+96
-170
circle/dashboard/static/dashboard/dashboard.less
+23
-1
circle/dashboard/tables.py
+25
-18
circle/dashboard/templates/dashboard/group-detail.html
+1
-1
circle/dashboard/templates/dashboard/index-nodes.html
+1
-1
circle/dashboard/templates/dashboard/index-templates.html
+19
-7
circle/dashboard/templates/dashboard/index-users.html
+53
-0
circle/dashboard/templates/dashboard/index.html
+6
-0
circle/dashboard/templates/dashboard/profile.html
+19
-2
circle/dashboard/templates/dashboard/user-create.html
+1
-2
circle/dashboard/templates/dashboard/user-list.html
+45
-0
circle/dashboard/tests/test_views.py
+9
-6
circle/dashboard/urls.py
+6
-3
circle/dashboard/views/index.py
+9
-1
circle/dashboard/views/template.py
+13
-1
circle/dashboard/views/user.py
+94
-24
circle/dashboard/views/vm.py
+1
-0
circle/locale/hu/LC_MESSAGES/django.po
+0
-0
No files found.
circle/dashboard/forms.py
View file @
1e514d5b
...
@@ -59,7 +59,7 @@ from django.utils.translation import string_concat
...
@@ -59,7 +59,7 @@ from django.utils.translation import string_concat
from
.validators
import
domain_validator
from
.validators
import
domain_validator
from
dashboard.models
import
ConnectCommand
from
dashboard.models
import
ConnectCommand
,
create_profile
LANGUAGES_WITH_CODE
=
((
l
[
0
],
string_concat
(
l
[
1
],
" ("
,
l
[
0
],
")"
))
LANGUAGES_WITH_CODE
=
((
l
[
0
],
string_concat
(
l
[
1
],
" ("
,
l
[
0
],
")"
))
for
l
in
LANGUAGES
)
for
l
in
LANGUAGES
)
...
@@ -1257,10 +1257,19 @@ class CirclePasswordChangeForm(PasswordChangeForm):
...
@@ -1257,10 +1257,19 @@ class CirclePasswordChangeForm(PasswordChangeForm):
class
UserCreationForm
(
OrgUserCreationForm
):
class
UserCreationForm
(
OrgUserCreationForm
):
def
__init__
(
self
,
*
args
,
**
kwargs
):
choices
=
kwargs
.
pop
(
'choices'
)
group
=
kwargs
.
pop
(
'default'
)
super
(
UserCreationForm
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
self
.
fields
[
'groups'
]
=
forms
.
ModelMultipleChoiceField
(
queryset
=
choices
,
initial
=
[
group
],
required
=
False
,
label
=
_
(
'Groups'
))
class
Meta
:
class
Meta
:
model
=
User
model
=
User
fields
=
(
"username"
,
'email'
,
'first_name'
,
'last_name'
)
fields
=
(
"username"
,
'email'
,
'first_name'
,
'last_name'
,
'groups'
)
@property
@property
def
helper
(
self
):
def
helper
(
self
):
...
@@ -1275,8 +1284,39 @@ class UserCreationForm(OrgUserCreationForm):
...
@@ -1275,8 +1284,39 @@ class UserCreationForm(OrgUserCreationForm):
user
.
set_password
(
self
.
cleaned_data
[
"password1"
])
user
.
set_password
(
self
.
cleaned_data
[
"password1"
])
if
commit
:
if
commit
:
user
.
save
()
user
.
save
()
create_profile
(
user
)
user
.
groups
.
add
(
*
self
.
cleaned_data
[
"groups"
])
return
user
class
UserEditForm
(
forms
.
ModelForm
):
instance_limit
=
forms
.
IntegerField
(
label
=
_
(
'Instance limit'
),
min_value
=
0
,
widget
=
NumberInput
)
def
__init__
(
self
,
*
args
,
**
kwargs
):
super
(
UserEditForm
,
self
)
.
__init__
(
*
args
,
**
kwargs
)
self
.
fields
[
"instance_limit"
]
.
initial
=
(
self
.
instance
.
profile
.
instance_limit
)
class
Meta
:
model
=
User
fields
=
(
'email'
,
'first_name'
,
'last_name'
,
'instance_limit'
,
'is_active'
)
def
save
(
self
,
commit
=
True
):
user
=
super
(
UserEditForm
,
self
)
.
save
()
user
.
profile
.
instance_limit
=
(
self
.
cleaned_data
[
'instance_limit'
]
or
None
)
user
.
profile
.
save
()
return
user
return
user
@property
def
helper
(
self
):
helper
=
FormHelper
()
helper
.
add_input
(
Submit
(
"submit"
,
_
(
"Save"
)))
return
helper
class
AclUserOrGroupAddForm
(
forms
.
Form
):
class
AclUserOrGroupAddForm
(
forms
.
Form
):
name
=
forms
.
CharField
(
widget
=
autocomplete_light
.
TextWidget
(
name
=
forms
.
CharField
(
widget
=
autocomplete_light
.
TextWidget
(
...
@@ -1497,3 +1537,10 @@ class TemplateListSearchForm(forms.Form):
...
@@ -1497,3 +1537,10 @@ class TemplateListSearchForm(forms.Form):
data
=
self
.
data
.
copy
()
data
=
self
.
data
.
copy
()
data
[
'stype'
]
=
"owned"
data
[
'stype'
]
=
"owned"
self
.
data
=
data
self
.
data
=
data
class
UserListSearchForm
(
forms
.
Form
):
s
=
forms
.
CharField
(
widget
=
forms
.
TextInput
(
attrs
=
{
'class'
:
"form-control input-tags"
,
'placeholder'
:
_
(
"Search..."
)
}))
circle/dashboard/static/dashboard/dashboard.js
View file @
1e514d5b
...
@@ -154,156 +154,70 @@ $(function () {
...
@@ -154,156 +154,70 @@ $(function () {
addSliderMiscs
();
addSliderMiscs
();
/* search for vms */
/* search */
var
my_vms
=
[];
function
register_search
(
form
,
list
,
generateHTML
)
{
$
(
"#dashboard-vm-search-input"
).
keyup
(
function
(
e
)
{
var
my_vms
=
[];
// if my_vms is empty get a list of our vms
var
search_in_progress
=
false
;
if
(
my_vms
.
length
<
1
)
{
$
(
"#dashboard-vm-search-form button i"
).
addClass
(
"fa-spinner fa-spin"
);
form
.
find
(
'input'
).
keyup
(
function
(
e
)
{
if
(
search_in_progress
)
{
$
.
get
(
"/dashboard/vm/list/"
,
function
(
result
)
{
return
;
for
(
var
i
in
result
)
{
}
my_vms
.
push
({
// if my_vms is empty get a list of our vms
'pk'
:
result
[
i
].
pk
,
if
(
my_vms
.
length
<
1
)
{
'name'
:
result
[
i
].
name
,
search_in_progress
=
true
;
'state'
:
result
[
i
].
state
,
var
btn
=
form
.
find
(
'button'
);
'fav'
:
result
[
i
].
fav
,
btn
.
find
(
'i'
).
addClass
(
"fa-spinner fa-spin"
);
'host'
:
result
[
i
].
host
,
'icon'
:
result
[
i
].
icon
,
$
.
get
(
form
.
prop
(
'action'
),
function
(
result
)
{
'status'
:
result
[
i
].
status
,
search_in_progress
=
false
;
'owner'
:
result
[
i
].
owner
,
my_vms
=
result
;
});
$
(
this
).
trigger
(
"keyup"
);
}
btn
.
find
(
'i'
).
removeClass
(
"fa-spinner fa-spin"
).
addClass
(
"fa-search"
);
$
(
"#dashboard-vm-search-input"
).
trigger
(
"keyup"
);
});
$
(
"#dashboard-vm-search-form button i"
).
removeClass
(
"fa-spinner fa-spin"
).
addClass
(
"fa-search"
);
return
;
});
}
return
;
}
input
=
$
(
"#dashboard-vm-search-input"
).
val
().
toLowerCase
();
input
=
$
(
this
).
val
().
toLowerCase
();
var
search_result
=
[];
var
search_result
=
[];
var
html
=
''
;
for
(
var
i
in
my_vms
)
{
for
(
var
i
in
my_vms
)
{
if
(
my_vms
[
i
].
name
.
toLowerCase
().
indexOf
(
input
)
!=
-
1
||
if
(
my_vms
[
i
].
name
.
toLowerCase
().
indexOf
(
input
)
!=
-
1
||
my_vms
[
i
].
host
.
indexOf
(
input
)
!=
-
1
)
{
(
my_vms
[
i
].
host
&&
my_vms
[
i
].
host
.
indexOf
(
input
)
!=
-
1
)
||
search_result
.
push
(
my_vms
[
i
]);
(
my_vms
[
i
].
org_id
&&
my_vms
[
i
].
org_id
.
toLowerCase
().
indexOf
(
input
)
!=
-
1
)
)
{
search_result
.
push
(
my_vms
[
i
]);
}
}
}
}
search_result
.
sort
(
compareVmByFav
);
search_result
.
sort
(
compareVmByFav
);
for
(
i
=
0
;
i
<
5
&&
i
<
search_result
.
length
;
i
++
)
html
+=
generateVmHTML
(
search_result
[
i
].
pk
,
search_result
[
i
].
name
,
search_result
[
i
].
owner
?
search_result
[
i
].
owner
:
search_result
[
i
].
host
,
search_result
[
i
].
icon
,
search_result
[
i
].
status
,
search_result
[
i
].
fav
,
(
search_result
.
length
<
5
));
if
(
search_result
.
length
===
0
)
html
+=
'<div class="list-group-item list-group-item-last">'
+
gettext
(
"No result"
)
+
'</div>'
;
$
(
"#dashboard-vm-list"
).
html
(
html
);
$
(
'.title-favourite'
).
tooltip
({
'placement'
:
'right'
});
});
$
(
"#dashboard-vm-search-form"
).
submit
(
function
()
{
var
html
=
''
;
var
vm_list_items
=
$
(
"#dashboard-vm-list .list-group-item"
);
var
is_last
=
search_result
.
length
<
5
;
if
(
vm_list_items
.
length
==
1
&&
vm_list_items
.
first
().
prop
(
"href"
))
{
window
.
location
.
href
=
vm_list_items
.
first
().
prop
(
"href"
);
return
false
;
}
return
true
;
});
/* search for nodes */
for
(
i
=
0
;
i
<
5
&&
i
<
search_result
.
length
;
i
++
)
var
my_nodes
=
[];
html
+=
generateHTML
(
search_result
[
i
],
is_last
);
$
(
"#dashboard-node-search-input"
).
keyup
(
function
(
e
)
{
// if my_nodes is empty get a list of our nodes
if
(
my_nodes
.
length
<
1
)
{
$
.
ajaxSetup
(
{
"async"
:
false
}
);
$
.
get
(
"/dashboard/node/list/"
,
function
(
result
)
{
for
(
var
i
in
result
)
{
my_nodes
.
push
({
'name'
:
result
[
i
].
name
,
'icon'
:
result
[
i
].
icon
,
'status'
:
result
[
i
].
status
,
'label'
:
result
[
i
].
label
,
'url'
:
result
[
i
].
url
,
});
}
});
$
.
ajaxSetup
(
{
"async"
:
true
}
);
}
input
=
$
(
"#dashboard-node-search-input"
).
val
().
toLowerCase
();
if
(
search_result
.
length
===
0
)
var
search_result
=
[];
html
+=
'<div class="list-group-item list-group-item-last">'
+
gettext
(
"No result"
)
+
'</div>'
;
var
html
=
''
;
for
(
var
i
in
my_nodes
)
{
if
(
my_nodes
[
i
].
name
.
toLowerCase
().
indexOf
(
input
)
!=
-
1
)
{
search_result
.
push
(
my_nodes
[
i
]);
}
}
for
(
i
=
0
;
i
<
5
&&
i
<
search_result
.
length
;
i
++
)
html
+=
generateNodeHTML
(
search_result
[
i
].
name
,
search_result
[
i
].
icon
,
search_result
[
i
].
status
,
search_result
[
i
].
url
,
(
search_result
.
length
<
5
));
if
(
search_result
.
length
===
0
)
html
+=
'<div class="list-group-item list-group-item-last">'
+
gettext
(
"No result"
)
+
'</div>'
;
$
(
"#dashboard-node-list"
).
html
(
html
);
html
=
''
;
for
(
i
=
0
;
i
<
5
&&
i
<
search_result
.
length
;
i
++
)
html
+=
generateNodeTagHTML
(
search_result
[
i
].
name
,
search_result
[
i
].
icon
,
search_result
[
i
].
status
,
search_result
[
i
].
label
,
search_result
[
i
].
url
);
if
(
search_result
.
length
===
0
)
html
+=
'<div class="list-group-item list-group-item-last">'
+
gettext
(
"No result"
)
+
'</div>'
;
$
(
"#dashboard-node-taglist"
).
html
(
html
);
// if there is only one result and ENTER is pressed redirect
if
(
e
.
keyCode
==
13
&&
search_result
.
length
==
1
)
{
window
.
location
.
href
=
search_result
[
0
].
url
;
}
if
(
e
.
keyCode
==
13
&&
search_result
.
length
>
1
&&
input
.
length
>
0
)
{
window
.
location
.
href
=
"/dashboard/node/list/?s="
+
input
;
}
});
/* search for groups */
list
.
html
(
html
);
var
my_groups
=
[];
$
(
'.title-favourite'
).
tooltip
({
'placement'
:
'right'
});
$
(
"#dashboard-group-search-input"
).
keyup
(
function
(
e
)
{
});
// if my_groups is empty get a list of our groups
if
(
my_groups
.
length
<
1
)
{
$
.
ajaxSetup
(
{
"async"
:
false
}
);
$
.
get
(
"/dashboard/group/list/"
,
function
(
result
)
{
for
(
var
i
in
result
)
{
my_groups
.
push
({
'url'
:
result
[
i
].
url
,
'name'
:
result
[
i
].
name
,
});
}
});
$
.
ajaxSetup
(
{
"async"
:
true
}
);
}
input
=
$
(
"#dashboard-group-search-input"
).
val
().
toLowerCase
();
form
.
submit
(
function
()
{
var
search_result
=
[];
var
vm_list_items
=
list
.
find
(
".list-group-item"
);
var
html
=
''
;
if
(
vm_list_items
.
length
==
1
&&
vm_list_items
.
first
().
prop
(
"href"
))
{
for
(
var
i
in
my_groups
)
{
window
.
location
.
href
=
vm_list_items
.
first
().
prop
(
"href"
);
if
(
my_groups
[
i
].
name
.
toLowerCase
().
indexOf
(
input
)
!=
-
1
)
{
return
false
;
search_result
.
push
(
my_groups
[
i
]);
}
}
}
return
true
;
for
(
i
=
0
;
i
<
5
&&
i
<
search_result
.
length
;
i
++
)
});
html
+=
generateGroupHTML
(
search_result
[
i
].
url
,
search_result
[
i
].
name
,
search_result
.
length
<
5
);
}
if
(
search_result
.
length
===
0
)
html
+=
'<div class="list-group-item list-group-item-last">'
+
gettext
(
"No result"
)
+
'</div>'
;
register_search
(
$
(
"#dashboard-vm-search-form"
),
$
(
"#dashboard-vm-list"
),
generateVmHTML
);
$
(
"#dashboard-group-list"
).
html
(
html
);
register_search
(
$
(
"#dashboard-node-search-form"
),
$
(
"#dashboard-node-list"
),
generateNodeHTML
);
register_search
(
$
(
"#dashboard-group-search-form"
),
$
(
"#dashboard-group-list"
),
generateGroupHTML
);
// if there is only one result and ENTER is pressed redirect
register_search
(
$
(
"#dashboard-user-search-form"
),
$
(
"#dashboard-user-list"
),
generateUserHTML
);
if
(
e
.
keyCode
==
13
&&
search_result
.
length
==
1
)
{
register_search
(
$
(
"#dashboard-template-search-form"
),
$
(
"#dashboard-template-list"
),
generateTemplateHTML
);
window
.
location
.
href
=
search_result
[
0
].
url
;
}
if
(
e
.
keyCode
==
13
&&
search_result
.
length
>
1
&&
input
.
length
>
0
)
{
window
.
location
.
href
=
"/dashboard/group/list/?s="
+
input
;
}
});
/* notification message toggle */
/* notification message toggle */
$
(
document
).
on
(
'click'
,
".notification-message-subject"
,
function
()
{
$
(
document
).
on
(
'click'
,
".notification-message-subject"
,
function
()
{
...
@@ -325,7 +239,7 @@ $(function () {
...
@@ -325,7 +239,7 @@ $(function () {
favicon
.
reset
();
favicon
.
reset
();
});
});
/* on the client confirmation button fire the clientInstalledAction */
/* on the client confirmation button fire the clientInstalledAction */
$
(
document
).
on
(
"click"
,
"#client-check-button"
,
function
(
event
)
{
$
(
document
).
on
(
"click"
,
"#client-check-button"
,
function
(
event
)
{
var
connectUri
=
$
(
'#connect-uri'
).
val
();
var
connectUri
=
$
(
'#connect-uri'
).
val
();
...
@@ -364,40 +278,52 @@ $(function () {
...
@@ -364,40 +278,52 @@ $(function () {
});
});
});
});
function
generateVmHTML
(
pk
,
name
,
host
,
icon
,
_status
,
fav
,
is_last
)
{
function
generateVmHTML
(
data
,
is_last
)
{
return
'<a href="
/dashboard/vm/'
+
pk
+
'/
" class="list-group-item'
+
return
'<a href="
'
+
data
.
url
+
'
" class="list-group-item'
+
(
is_last
?
' list-group-item-last'
:
''
)
+
'">'
+
(
is_last
?
' list-group-item-last'
:
''
)
+
'">'
+
'<span class="index-vm-list-name">'
+
'<span class="index-vm-list-name">'
+
'<i class="fa '
+
icon
+
'" title="'
+
_status
+
'"></i> '
+
safe_tags_replace
(
name
)
+
'<i class="fa '
+
data
.
icon
+
'" title="'
+
data
.
status
+
'"></i> '
+
safe_tags_replace
(
data
.
name
)
+
'</span>'
+
'</span>'
+
'<small class="text-muted index-vm-list-host"> '
+
host
+
'</small>'
+
'<small class="text-muted index-vm-list-host"> '
+
data
.
host
+
'</small>'
+
'<div class="pull-right dashboard-vm-favourite" data-vm="'
+
pk
+
'">'
+
'<div class="pull-right dashboard-vm-favourite" data-vm="'
+
data
.
pk
+
'">'
+
(
fav
?
'<i class="fa fa-star text-primary title-favourite" title="'
+
gettext
(
"Unfavourite"
)
+
'"></i>'
:
(
data
.
fav
?
'<i class="fa fa-star text-primary title-favourite" title="'
+
gettext
(
"Unfavourite"
)
+
'"></i>'
:
'<i class="fa fa-star-o text-primary title-favourite" title="'
+
gettext
(
"Mark as favorite"
)
+
'"></i>'
)
+
'<i class="fa fa-star-o text-primary title-favourite" title="'
+
gettext
(
"Mark as favorite"
)
+
'"></i>'
)
+
'</div>'
+
'</div>'
+
'<div style="clear: both;"></div>'
+
'<div style="clear: both;"></div>'
+
'</a>'
;
'</a>'
;
}
}
function
generateGroupHTML
(
url
,
name
,
is_last
)
{
function
generateGroupHTML
(
data
,
is_last
)
{
return
'<a href="'
+
url
+
'" class="list-group-item real-link'
+
(
is_last
?
" list-group-item-last"
:
""
)
+
'">'
+
return
'<a href="'
+
data
.
url
+
'" class="list-group-item real-link'
+
(
is_last
?
" list-group-item-last"
:
""
)
+
'">'
+
'<i class="fa fa-users"></i> '
+
safe_tags_replace
(
name
)
+
'<i class="fa fa-users"></i> '
+
safe_tags_replace
(
data
.
name
)
+
'</a>'
;
'</a>'
;
}
}
function
generateNodeHTML
(
name
,
icon
,
_status
,
url
,
is_last
)
{
function
generateUserHTML
(
data
,
is_last
)
{
return
'<a href="'
+
url
+
'" class="list-group-item real-link'
+
(
is_last
?
' list-group-item-last'
:
''
)
+
'">'
+
return
'<a href="'
+
data
.
url
+
'" class="list-group-item real-link'
+
(
is_last
?
" list-group-item-last"
:
""
)
+
'">'
+
'<span class="index-node-list-name">'
+
'<span class="index-user-list-name"><i class="fa fa-user"></i> '
+
safe_tags_replace
(
data
.
name
)
+
'</span>'
+
'<i class="fa '
+
icon
+
'" title="'
+
_status
+
'"></i> '
+
safe_tags_replace
(
name
)
+
'<span class="index-user-list-org">'
+
'</span>'
+
'<small class="text-muted"> '
+
(
data
.
org_id
?
safe_tags_replace
(
data
.
org_id
)
:
""
)
+
'</small>'
+
'<div style="clear: both;"></div>'
+
'</span></a>'
;
'</a>'
;
}
}
function
generateNodeTagHTML
(
name
,
icon
,
_status
,
label
,
url
)
{
function
generateTemplateHTML
(
data
,
is_last
)
{
return
'<a href="'
+
url
+
'" class="label '
+
label
+
'" >'
+
return
'<a href="'
+
data
.
url
+
'" class="list-group-item real-link'
+
(
is_last
?
" list-group-item-last"
:
""
)
+
'">'
+
'<i class="fa '
+
icon
+
'" title="'
+
_status
+
'"></i> '
+
safe_tags_replace
(
name
)
+
' <span class="index-template-list-name">'
+
'</a> '
;
' <i class="fa fa-'
+
data
.
icon
+
'"></i> '
+
safe_tags_replace
(
data
.
name
)
+
' </span>'
+
' <small class="text-muted index-template-list-system">'
+
safe_tags_replace
(
data
.
system
)
+
'</small>'
+
' <div class="clearfix"></div>'
+
'</a>'
;
}
function
generateNodeHTML
(
data
,
is_last
)
{
return
'<a href="'
+
data
.
url
+
'" class="list-group-item real-link'
+
(
is_last
?
' list-group-item-last'
:
''
)
+
'">'
+
'<span class="index-node-list-name">'
+
'<i class="fa '
+
data
.
icon
+
'" title="'
+
data
.
status
+
'"></i> '
+
safe_tags_replace
(
data
.
name
)
+
'</span>'
+
'<div style="clear: both;"></div>'
+
'</a>'
;
}
}
/* copare vm-s by fav, pk order */
/* copare vm-s by fav, pk order */
...
...
circle/dashboard/static/dashboard/dashboard.less
View file @
1e514d5b
...
@@ -562,7 +562,7 @@ footer a, footer a:hover, footer a:visited {
...
@@ -562,7 +562,7 @@ footer a, footer a:hover, footer a:visited {
}
}
#dashboard-vm-list, #dashboard-node-list, #dashboard-group-list,
#dashboard-vm-list, #dashboard-node-list, #dashboard-group-list,
#dashboard-template-list, #dashboard-files-toplist {
#dashboard-template-list, #dashboard-files-toplist
, #dashboard-user-list
{
min-height: 200px;
min-height: 200px;
}
}
...
@@ -1168,6 +1168,28 @@ textarea[name="new_members"] {
...
@@ -1168,6 +1168,28 @@ textarea[name="new_members"] {
}
}
}
}
#dashboard-user-list {
.list-group-item {
display: flex;
}
.index-user-list-name, .index-user-list-org {
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.index-user-list-name {
max-width: 80%;
}
.index-user-list-org {
padding-left: 5px;
flex: 1;
}
}
.fa-fw-12 {
.fa-fw-12 {
/* fa-fw is too wide */
/* fa-fw is too wide */
width: 12px;
width: 12px;
...
...
circle/dashboard/tables.py
View file @
1e514d5b
...
@@ -18,13 +18,16 @@
...
@@ -18,13 +18,16 @@
from
__future__
import
absolute_import
from
__future__
import
absolute_import
from
django.contrib.auth.models
import
Group
,
User
from
django.contrib.auth.models
import
Group
,
User
from
django.utils.translation
import
ugettext_lazy
as
_
from
django.utils.html
import
mark_safe
from
django_tables2
import
Table
,
A
from
django_tables2
import
Table
,
A
from
django_tables2.columns
import
(
TemplateColumn
,
Column
,
LinkColumn
,
from
django_tables2.columns
import
(
BooleanColumn
)
TemplateColumn
,
Column
,
LinkColumn
,
BooleanColumn
)
from
django_sshkey.models
import
UserKey
from
vm.models
import
Node
,
InstanceTemplate
,
Lease
from
vm.models
import
Node
,
InstanceTemplate
,
Lease
from
django.utils.translation
import
ugettext_lazy
as
_
from
django_sshkey.models
import
UserKey
from
dashboard.models
import
ConnectCommand
from
dashboard.models
import
ConnectCommand
...
@@ -123,26 +126,30 @@ class GroupListTable(Table):
...
@@ -123,26 +126,30 @@ class GroupListTable(Table):
class
UserListTable
(
Table
):
class
UserListTable
(
Table
):
pk
=
TemplateColumn
(
username
=
LinkColumn
(
template_name
=
'dashboard/vm-list/column-id.html'
,
'dashboard.views.profile'
,
verbose_name
=
"ID"
,
args
=
[
A
(
'username'
)],
attrs
=
{
'th'
:
{
'class'
:
'vm-list-table-thin'
}},
)
)
profile__org_id
=
LinkColumn
(
username
=
TemplateColumn
(
'dashboard.views.profile'
,
template_name
=
"dashboard/group-list/column-username.html"
accessor
=
'profile.org_id'
,
args
=
[
A
(
'username'
)],
verbose_name
=
_
(
'Organization ID'
)
)
)
class
Meta
:
is_superuser
=
BooleanColumn
(
model
=
User
verbose_name
=
mark_safe
(
attrs
=
{
'class'
:
(
'table table-bordered table-striped table-hover '
_
(
'<abbr data-placement="left" title="Superuser status">SU</abbr>'
)
'vm-list-table'
)}
)
fields
=
(
'pk'
,
'username'
,
)
)
is_active
=
BooleanColumn
()
class
UserListTablex
(
Table
):
class
Meta
:
class
Meta
:
model
=
User
model
=
User
template
=
"django_tables2/table_no_page.html"
attrs
=
{
'class'
:
(
'table table-bordered table-striped table-hover'
)}
fields
=
(
'username'
,
'last_name'
,
'first_name'
,
'profile__org_id'
,
'email'
,
'is_active'
,
'is_superuser'
)
class
TemplateListTable
(
Table
):
class
TemplateListTable
(
Table
):
...
...
circle/dashboard/templates/dashboard/group-detail.html
View file @
1e514d5b
...
@@ -78,7 +78,7 @@
...
@@ -78,7 +78,7 @@
<h3>
<h3>
{% trans "User list" %}
{% trans "User list" %}
{% if perms.auth.add_user %}
{% if perms.auth.add_user %}
<a
href=
"{% url "
dashboard
.
views
.
create-user
"
group
.
pk
%
}"
class=
"btn btn-success pull-right"
>
<a
href=
"{% url "
dashboard
.
views
.
user-create
"
%}?
group_pk=
{{
group
.
pk
}
}"
class=
"btn btn-success pull-right"
>
{% trans "Create user" %}
{% trans "Create user" %}
</a>
</a>
{% endif %}
{% endif %}
...
...
circle/dashboard/templates/dashboard/index-nodes.html
View file @
1e514d5b
...
@@ -37,7 +37,7 @@
...
@@ -37,7 +37,7 @@
id=
"dashboard-node-search-form"
>
id=
"dashboard-node-search-form"
>
<div
class=
"input-group input-group-sm"
>
<div
class=
"input-group input-group-sm"
>
<input
id=
"dashboard-node-search-input"
type=
"text"
class=
"form-control"
<input
id=
"dashboard-node-search-input"
type=
"text"
class=
"form-control"
placeholder=
"{% trans "
Search
..."
%}"
/>
name=
"s"
placeholder=
"{% trans "
Search
..."
%}"
/>
<div
class=
"input-group-btn"
>
<div
class=
"input-group-btn"
>
<button
type=
"submit"
class=
"btn btn-primary"
title=
"{% trans "
Search
"
%}"
data-container=
"body"
>
<button
type=
"submit"
class=
"btn btn-primary"
title=
"{% trans "
Search
"
%}"
data-container=
"body"
>
<i
class=
"fa fa-search"
></i>
<i
class=
"fa fa-search"
></i>
...
...
circle/dashboard/templates/dashboard/index-templates.html
View file @
1e514d5b
...
@@ -33,13 +33,25 @@
...
@@ -33,13 +33,25 @@
{% endfor %}
{% endfor %}
</div>
</div>
<div
class=
"list-group-item list-group-footer"
>
<div
class=
"list-group-item list-group-footer"
>
<div
class=
"text-right"
>
<div
class=
"row"
>
<a
href=
"{% url "
dashboard
.
views
.
template-list
"
%}"
class=
"btn btn-primary btn-xs"
>
<div
class=
"col-xs-6"
>
<i
class=
"fa fa-chevron-circle-right"
></i>
{% trans "show all" %}
<form
action=
"{% url "
dashboard
.
views
.
template-list
"
%}"
method=
"GET"
id=
"dashboard-template-search-form"
>
</a>
<div
class=
"input-group input-group-sm"
>
<a
href=
"{% url "
dashboard
.
views
.
template-choose
"
%}"
class=
"btn btn-success btn-xs template-choose"
>
<input
id=
"dashboard-group-search-input"
name=
"s"
type=
"text"
class=
"form-control"
placeholder=
"{% trans "
Search
..."
%}"
/>
<i
class=
"fa fa-plus-circle"
></i>
{% trans "new" %}
<div
class=
"input-group-btn"
>
</a>
<button
type=
"submit"
class=
"btn btn-primary"
><i
class=
"fa fa-search"
></i></button>
</div>
</div>
</form>
</div>
<div
class=
"col-xs-6 text-right"
>
<a
href=
"{% url "
dashboard
.
views
.
template-list
"
%}"
class=
"btn btn-primary btn-xs"
>
<i
class=
"fa fa-chevron-circle-right"
></i>
{% trans "show all" %}
</a>
<a
href=
"{% url "
dashboard
.
views
.
template-choose
"
%}"
class=
"btn btn-success btn-xs template-choose"
>
<i
class=
"fa fa-plus-circle"
></i>
{% trans "new" %}
</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
...
...
circle/dashboard/templates/dashboard/index-users.html
0 → 100644
View file @
1e514d5b
{% load i18n %}
<div
class=
"panel panel-default"
>
<div
class=
"panel-heading"
>
<div
class=
"pull-right toolbar"
>
<span
class=
"btn btn-default btn-xs infobtn"
data-container=
"body"
title=
"{% trans "
List
of
CIRCLE
users
."
%}"
><i
class=
"fa fa-info-circle"
></i></span>
</div>
<h3
class=
"no-margin"
><i
class=
"fa fa-users"
></i>
{% trans "Users" %}
</h3>
</div>
<div
class=
"list-group"
id=
"user-list-view"
>
<div
id=
"dashboard-user-list"
>
{% for i in users %}
<a
href=
"{% url "
dashboard
.
views
.
profile
"
username=
i.username
%}"
class=
"list-group-item real-link
{% if forloop.last and users|length < 5 %} list-group-item-last{% endif %}"
>
<span
class=
"index-user-list-name"
>
<i
class=
"fa fa-user"
></i>
{% firstof i.get_full_name|safe i.username|safe %}
</span>
<span
class=
"index-user-list-org"
>
<small
class=
"text-muted"
>
{{ i.profile.org_id|default:"" }}
</small>
</span>
</a>
{% endfor %}
</div>
<div
class=
"list-group-item list-group-footer text-right"
>
<div
class=
"row"
>
<div
class=
"col-xs-6"
>
<form
action=
"{% url "
dashboard
.
views
.
user-list
"
%}"
method=
"GET"
id=
"dashboard-user-search-form"
>
<div
class=
"input-group input-group-sm"
>
<input
id=
"dashboard-group-search-input"
name=
"s"
type=
"text"
class=
"form-control"
placeholder=
"{% trans "
Search
..."
%}"
/>
<div
class=
"input-group-btn"
>
<button
type=
"submit"
class=
"btn btn-primary"
><i
class=
"fa fa-search"
></i></button>
</div>
</div>
</form>
</div>
<div
class=
"col-xs-6 text-right"
>
<a
class=
"btn btn-primary btn-xs"
href=
"{% url "
dashboard
.
views
.
user-list
"
%}"
>
<i
class=
"fa fa-chevron-circle-right"
></i>
{% if more_users > 0 %}
{% blocktrans count more=more_users %}
<strong>
{{ more }}
</strong>
more
{% plural %}
<strong>
{{ more }}
</strong>
more
{% endblocktrans %}
{% else %}
{% trans "list" %}
{% endif %}
</a>
<a
class=
"btn btn-success btn-xs user-create"
href=
"{% url "
dashboard
.
views
.
user-create
"
%}"
><i
class=
"fa fa-plus-circle"
></i>
{% trans "new" %}
</a>
</div>
</div>
</div>
</div>
</div>
circle/dashboard/templates/dashboard/index.html
View file @
1e514d5b
...
@@ -48,6 +48,12 @@
...
@@ -48,6 +48,12 @@
{% include "dashboard/index-nodes.html" %}
{% include "dashboard/index-nodes.html" %}
</div>
</div>
{% endif %}
{% endif %}
{% if perms.auth.change_user %}
<div
class=
"col-lg-4 col-sm-6"
>
{% include "dashboard/index-users.html" %}
</div>
{% endif %}
</div>
</div>
</div>
</div>
{% endblock %}
{% endblock %}
circle/dashboard/templates/dashboard/profile.html
View file @
1e514d5b
...
@@ -8,7 +8,7 @@
...
@@ -8,7 +8,7 @@
{% block content %}
{% block content %}
<div
class=
"row"
>
<div
class=
"row"
>
<div
class=
"col-md-
12
"
>
<div
class=
"col-md-
{% if perms.auth.change_user %}8{% else %}12{% endif %}
"
>
<div
class=
"panel panel-default"
>
<div
class=
"panel panel-default"
>
<div
class=
"panel-heading"
>
<div
class=
"panel-heading"
>
{% if request.user.is_superuser %}
{% if request.user.is_superuser %}
...
@@ -17,7 +17,7 @@
...
@@ -17,7 +17,7 @@
title=
"{% trans "
Log
in
as
this
user
.
Recommended
to
open
in
an
incognito
window
."
%}"
>
title=
"{% trans "
Log
in
as
this
user
.
Recommended
to
open
in
an
incognito
window
."
%}"
>
{% trans "Login as this user" %}
</a>
{% trans "Login as this user" %}
</a>
{% endif %}
{% endif %}
<a
class=
"pull-right btn btn-default btn-xs"
href=
"{% url "
dashboard
.
index
"
%}"
>
{% trans "Back" %}
</a>
<a
class=
"pull-right btn btn-default btn-xs"
href=
"{% url "
dashboard
.
views
.
user-list
"
%}"
>
{% trans "Back" %}
</a>
<h3
class=
"no-margin"
>
<h3
class=
"no-margin"
>
<i
class=
"fa fa-user"
></i>
<i
class=
"fa fa-user"
></i>
{% include "dashboard/_display-name.html" with user=profile show_org=True %}
{% include "dashboard/_display-name.html" with user=profile show_org=True %}
...
@@ -109,6 +109,23 @@
...
@@ -109,6 +109,23 @@
</div>
</div>
</div>
</div>
</div>
</div>
{% if perms.auth.change_user %}
<div
class=
"col-md-4"
>
<div
class=
"panel panel-default"
>
<div
class=
"panel-heading"
>
<h3
class=
"no-margin"
>
<i
class=
"fa fa-user"
></i>
{% trans "Edit user" %}
</h3>
</div>
<div
class=
"panel-body"
>
{% crispy form %}
</div>
</div>
</div>
{% endif %}
</div>
</div>
{% endblock %}
{% endblock %}
circle/dashboard/templates/dashboard/user-create.html
View file @
1e514d5b
{% extends "dashboard/base.html" %}
{% load i18n %}
{% load i18n %}
{% load crispy_forms_tags %}
{% load crispy_forms_tags %}
{% block content %}
{% block content %}
{% crispy form %}
{% crispy form %}
{% endblock %}
{% endblock %}
circle/dashboard/templates/dashboard/user-list.html
0 → 100644
View file @
1e514d5b
{% extends "dashboard/base.html" %}
{% load staticfiles %}
{% load i18n %}
{% load render_table from django_tables2 %}
{% block title-page %}{% trans "Users" %}{% 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
.
user-create
"
%}"
class=
"pull-right btn btn-success btn-xs"
>
<i
class=
"fa fa-plus"
></i>
{% trans "new user" %}
</a>
<h3
class=
"no-margin"
><i
class=
"fa fa-user"
></i>
{% trans "Users" %}
</h3>
</div>
<div
class=
"panel-body"
>
<div
class=
"row"
>
<div
class=
"col-md-offset-8 col-md-4"
id=
"user-list-search"
>
<form
action=
""
method=
"GET"
>
<div
class=
"input-group"
>
{{ search_form.s }}
<div
class=
"input-group-btn"
>
{{ search_form.stype }}
<button
type=
"submit"
class=
"btn btn-primary input-tags"
>
<i
class=
"fa fa-search"
></i>
</button>
</div>
</div>
<!-- .input-group -->
</form>
</div>
<!-- .col-md-4 #user-list-search -->
</div>
</div>
<div
class=
"panel-body"
>
<div
class=
"table-responsive"
>
{% render_table table %}
</div>
</div>
</div>
</div>
</div>
{% endblock %}
circle/dashboard/tests/test_views.py
View file @
1e514d5b
...
@@ -1293,24 +1293,26 @@ class GroupDetailTest(LoginMixin, TestCase):
...
@@ -1293,24 +1293,26 @@ class GroupDetailTest(LoginMixin, TestCase):
self
.
login
(
c
,
'user1'
)
self
.
login
(
c
,
'user1'
)
self
.
u1
.
user_permissions
.
add
(
Permission
.
objects
.
get
(
self
.
u1
.
user_permissions
.
add
(
Permission
.
objects
.
get
(
name
=
'Can add user'
))
name
=
'Can add user'
))
response
=
c
.
post
(
'/dashboard/
group/
%
d/create/'
%
self
.
g1
.
pk
,
response
=
c
.
post
(
'/dashboard/
profile/create/'
,
{
'username'
:
'userx1'
,
{
'username'
:
'userx1'
,
'groups'
:
self
.
g1
.
pk
,
'password1'
:
'test123'
,
'password1'
:
'test123'
,
'password2'
:
'test123'
})
'password2'
:
'test123'
})
self
.
assertEqual
(
response
.
status_code
,
403
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
user_count
,
self
.
g1
.
user_set
.
count
())
self
.
assertEqual
(
user_count
,
self
.
g1
.
user_set
.
count
())
def
test_permitted_user_add_wo_can_add_user_perm
(
self
):
def
test_permitted_user_add_wo_can_add_user_perm
(
self
):
user_count
=
self
.
g1
.
user_set
.
count
()
user_count
=
self
.
g1
.
user_set
.
count
()
c
=
Client
()
c
=
Client
()
self
.
login
(
c
,
'user0'
)
self
.
login
(
c
,
'user0'
)
response
=
c
.
post
(
'/dashboard/
group/
%
d/create/'
%
self
.
g1
.
pk
,
response
=
c
.
post
(
'/dashboard/
profile/create/'
,
{
'username'
:
'userx2'
,
{
'username'
:
'userx2'
,
'groups'
:
self
.
g1
.
pk
,
'password1'
:
'test123'
,
'password1'
:
'test123'
,
'password2'
:
'test123'
})
'password2'
:
'test123'
})
self
.
assertRedirects
(
self
.
assertRedirects
(
response
,
response
,
'/accounts/login/?next=/dashboard/
group/
%
d/create/'
%
self
.
g1
.
pk
)
'/accounts/login/?next=/dashboard/
profile/create/'
)
self
.
assertEqual
(
response
.
status_code
,
302
)
self
.
assertEqual
(
response
.
status_code
,
302
)
self
.
assertEqual
(
user_count
,
self
.
g1
.
user_set
.
count
())
self
.
assertEqual
(
user_count
,
self
.
g1
.
user_set
.
count
())
...
@@ -1320,11 +1322,12 @@ class GroupDetailTest(LoginMixin, TestCase):
...
@@ -1320,11 +1322,12 @@ class GroupDetailTest(LoginMixin, TestCase):
name
=
'Can add user'
))
name
=
'Can add user'
))
c
=
Client
()
c
=
Client
()
self
.
login
(
c
,
'user0'
)
self
.
login
(
c
,
'user0'
)
response
=
c
.
post
(
'/dashboard/
group/
%
d/create/'
%
self
.
g1
.
pk
,
response
=
c
.
post
(
'/dashboard/
profile/create/'
,
{
'username'
:
'userx2'
,
{
'username'
:
'userx2'
,
'groups'
:
self
.
g1
.
pk
,
'password1'
:
'test123'
,
'password1'
:
'test123'
,
'password2'
:
'test123'
})
'password2'
:
'test123'
})
self
.
assertRedirects
(
response
,
'/dashboard/
group/
%
d/'
%
self
.
g1
.
pk
)
self
.
assertRedirects
(
response
,
'/dashboard/
profile/userx2/'
)
self
.
assertEqual
(
user_count
+
1
,
self
.
g1
.
user_set
.
count
())
self
.
assertEqual
(
user_count
+
1
,
self
.
g1
.
user_set
.
count
())
self
.
assertEqual
(
response
.
status_code
,
302
)
self
.
assertEqual
(
response
.
status_code
,
302
)
...
...
circle/dashboard/urls.py
View file @
1e514d5b
...
@@ -52,6 +52,7 @@ from .views import (
...
@@ -52,6 +52,7 @@ from .views import (
TransferTemplateOwnershipView
,
TransferTemplateOwnershipConfirmView
,
TransferTemplateOwnershipView
,
TransferTemplateOwnershipConfirmView
,
OpenSearchDescriptionView
,
OpenSearchDescriptionView
,
NodeActivityView
,
NodeActivityView
,
UserList
,
)
)
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
...
@@ -61,6 +62,11 @@ autocomplete_light.autodiscover()
...
@@ -61,6 +62,11 @@ autocomplete_light.autodiscover()
urlpatterns
=
patterns
(
urlpatterns
=
patterns
(
''
,
''
,
url
(
r'^$'
,
IndexView
.
as_view
(),
name
=
"dashboard.index"
),
url
(
r'^$'
,
IndexView
.
as_view
(),
name
=
"dashboard.index"
),
url
(
r"^profile/list/$"
,
UserList
.
as_view
(),
name
=
"dashboard.views.user-list"
),
url
(
r'^profile/create/$'
,
UserCreationView
.
as_view
(),
name
=
"dashboard.views.user-create"
),
url
(
r'^lease/(?P<pk>\d+)/$'
,
LeaseDetail
.
as_view
(),
url
(
r'^lease/(?P<pk>\d+)/$'
,
LeaseDetail
.
as_view
(),
name
=
"dashboard.views.lease-detail"
),
name
=
"dashboard.views.lease-detail"
),
url
(
r'^lease/create/$'
,
LeaseCreate
.
as_view
(),
url
(
r'^lease/create/$'
,
LeaseCreate
.
as_view
(),
...
@@ -174,9 +180,6 @@ urlpatterns = patterns(
...
@@ -174,9 +180,6 @@ urlpatterns = patterns(
name
=
"dashboard.views.remove-future-user"
),
name
=
"dashboard.views.remove-future-user"
),
url
(
r'^group/create/$'
,
GroupCreate
.
as_view
(),
url
(
r'^group/create/$'
,
GroupCreate
.
as_view
(),
name
=
'dashboard.views.group-create'
),
name
=
'dashboard.views.group-create'
),
url
(
r'^group/(?P<group_pk>\d+)/create/$'
,
UserCreationView
.
as_view
(),
name
=
"dashboard.views.create-user"
),
url
(
r'^group/(?P<group_pk>\d+)/permissions/$'
,
url
(
r'^group/(?P<group_pk>\d+)/permissions/$'
,
GroupPermissionsView
.
as_view
(),
GroupPermissionsView
.
as_view
(),
name
=
"dashboard.views.group-permissions"
),
name
=
"dashboard.views.group-permissions"
),
...
...
circle/dashboard/views/index.py
View file @
1e514d5b
...
@@ -21,7 +21,7 @@ import logging
...
@@ -21,7 +21,7 @@ import logging
from
django.core.cache
import
get_cache
from
django.core.cache
import
get_cache
from
django.core.urlresolvers
import
reverse
from
django.core.urlresolvers
import
reverse
from
django.conf
import
settings
from
django.conf
import
settings
from
django.contrib.auth.models
import
Group
from
django.contrib.auth.models
import
Group
,
User
from
django.views.generic
import
TemplateView
from
django.views.generic
import
TemplateView
from
braces.views
import
LoginRequiredMixin
from
braces.views
import
LoginRequiredMixin
...
@@ -86,6 +86,14 @@ class IndexView(LoginRequiredMixin, TemplateView):
...
@@ -86,6 +86,14 @@ class IndexView(LoginRequiredMixin, TemplateView):
'more_groups'
:
groups
.
count
()
-
len
(
groups
[:
5
]),
'more_groups'
:
groups
.
count
()
-
len
(
groups
[:
5
]),
})
})
# users
if
user
.
has_module_perms
(
'auth.change_user'
):
users
=
User
.
objects
.
all
()
context
.
update
({
'users'
:
users
[:
5
],
'more_users'
:
users
.
count
()
-
len
(
users
[:
5
]),
})
# template
# template
if
user
.
has_perm
(
'vm.create_template'
):
if
user
.
has_perm
(
'vm.create_template'
):
context
[
'templates'
]
=
InstanceTemplate
.
get_objects_with_level
(
context
[
'templates'
]
=
InstanceTemplate
.
get_objects_with_level
(
...
...
circle/dashboard/views/template.py
View file @
1e514d5b
...
@@ -207,7 +207,19 @@ class TemplateList(LoginRequiredMixin, FilterMixin, SingleTableView):
...
@@ -207,7 +207,19 @@ class TemplateList(LoginRequiredMixin, FilterMixin, SingleTableView):
def
get
(
self
,
*
args
,
**
kwargs
):
def
get
(
self
,
*
args
,
**
kwargs
):
self
.
search_form
=
TemplateListSearchForm
(
self
.
request
.
GET
)
self
.
search_form
=
TemplateListSearchForm
(
self
.
request
.
GET
)
self
.
search_form
.
full_clean
()
self
.
search_form
.
full_clean
()
return
super
(
TemplateList
,
self
)
.
get
(
*
args
,
**
kwargs
)
if
self
.
request
.
is_ajax
():
templates
=
[{
'icon'
:
i
.
os_type
,
'system'
:
i
.
system
,
'url'
:
reverse
(
"dashboard.views.template-detail"
,
kwargs
=
{
'pk'
:
i
.
pk
}),
'name'
:
i
.
name
}
for
i
in
self
.
get_queryset
()]
return
HttpResponse
(
json
.
dumps
(
templates
),
content_type
=
"application/json"
,
)
else
:
return
super
(
TemplateList
,
self
)
.
get
(
*
args
,
**
kwargs
)
def
create_acl_queryset
(
self
,
model
):
def
create_acl_queryset
(
self
,
model
):
queryset
=
super
(
TemplateList
,
self
)
.
create_acl_queryset
(
model
)
queryset
=
super
(
TemplateList
,
self
)
.
create_acl_queryset
(
model
)
...
...
circle/dashboard/views/user.py
View file @
1e514d5b
...
@@ -31,25 +31,31 @@ from django.core.exceptions import (
...
@@ -31,25 +31,31 @@ from django.core.exceptions import (
)
)
from
django.core.urlresolvers
import
reverse
,
reverse_lazy
from
django.core.urlresolvers
import
reverse
,
reverse_lazy
from
django.core.paginator
import
Paginator
,
InvalidPage
from
django.core.paginator
import
Paginator
,
InvalidPage
from
django.db.models
import
Q
from
django.http
import
HttpResponse
,
HttpResponseRedirect
,
Http404
from
django.http
import
HttpResponse
,
HttpResponseRedirect
,
Http404
from
django.shortcuts
import
redirect
,
get_object_or_404
from
django.shortcuts
import
redirect
,
get_object_or_404
from
django.utils.translation
import
ugettext
as
_
from
django.utils.translation
import
ugettext
as
_
from
django.views.decorators.http
import
require_POST
from
django.views.decorators.http
import
require_POST
from
django.views.generic
import
(
from
django.views.generic
import
(
TemplateView
,
DetailView
,
View
,
UpdateView
,
CreateView
,
TemplateView
,
View
,
UpdateView
,
CreateView
,
)
)
from
django_sshkey.models
import
UserKey
from
django_sshkey.models
import
UserKey
from
braces.views
import
LoginRequiredMixin
,
PermissionRequiredMixin
from
braces.views
import
LoginRequiredMixin
,
PermissionRequiredMixin
from
django_tables2
import
SingleTableView
from
vm.models
import
Instance
,
InstanceTemplate
from
vm.models
import
Instance
,
InstanceTemplate
from
..forms
import
(
from
..forms
import
(
CircleAuthenticationForm
,
MyProfileForm
,
UserCreationForm
,
UnsubscribeForm
,
CircleAuthenticationForm
,
MyProfileForm
,
UserCreationForm
,
UnsubscribeForm
,
UserKeyForm
,
CirclePasswordChangeForm
,
ConnectCommandForm
,
UserKeyForm
,
CirclePasswordChangeForm
,
ConnectCommandForm
,
UserListSearchForm
,
UserEditForm
,
)
from
..models
import
Profile
,
GroupProfile
,
ConnectCommand
from
..tables
import
(
UserKeyListTable
,
ConnectCommandListTable
,
UserListTable
,
)
)
from
..models
import
Profile
,
GroupProfile
,
ConnectCommand
,
create_profile
from
..tables
import
UserKeyListTable
,
ConnectCommandListTable
from
.util
import
saml_available
,
DeleteViewBase
from
.util
import
saml_available
,
DeleteViewBase
...
@@ -267,36 +273,46 @@ class UserCreationView(LoginRequiredMixin, PermissionRequiredMixin,
...
@@ -267,36 +273,46 @@ class UserCreationView(LoginRequiredMixin, PermissionRequiredMixin,
template_name
=
'dashboard/user-create.html'
template_name
=
'dashboard/user-create.html'
permission_required
=
"auth.add_user"
permission_required
=
"auth.add_user"
def
get_
success_url
(
self
):
def
get_
template_names
(
self
):
re
verse
(
'dashboard.views.group-detail'
,
args
=
[
self
.
group
.
pk
])
re
turn
[
'dashboard/nojs-wrapper.html'
]
def
get_group
(
self
,
group_pk
):
def
get_context_data
(
self
,
*
args
,
**
kwargs
):
self
.
group
=
get_object_or_404
(
Group
,
pk
=
group_pk
)
context
=
super
(
UserCreationView
,
self
)
.
get_context_data
(
*
args
,
if
not
self
.
group
.
profile
.
has_level
(
self
.
request
.
user
,
'owner'
):
**
kwargs
)
raise
PermissionDenied
()
context
.
update
({
'template'
:
self
.
template_name
,
'box_title'
:
_
(
'Create a User'
),
})
return
context
def
get
(
self
,
*
args
,
**
kwargs
):
def
get
_success_url
(
self
):
self
.
get_group
(
kwargs
.
pop
(
'group_pk'
)
)
return
reverse
(
'dashboard.views.profile'
,
args
=
[
self
.
object
.
username
]
)
return
super
(
UserCreationView
,
self
)
.
get
(
*
args
,
**
kwargs
)
def
get_form_kwargs
(
self
):
def
post
(
self
,
*
args
,
**
kwargs
):
profiles
=
GroupProfile
.
get_objects_with_level
(
group_pk
=
kwargs
.
pop
(
'group_pk'
)
'owner'
,
self
.
request
.
user
)
self
.
get_group
(
group_pk
)
choices
=
Group
.
objects
.
filter
(
groupprofile__in
=
profiles
)
ret
=
super
(
UserCreationView
,
self
)
.
post
(
*
args
,
**
kwargs
)
group_pk
=
self
.
request
.
GET
.
get
(
'group_pk'
)
if
self
.
object
:
if
group_pk
:
create_profile
(
self
.
object
)
try
:
self
.
object
.
groups
.
add
(
self
.
group
)
default
=
choices
.
get
(
pk
=
group_pk
)
return
redirect
(
except
(
ValueError
,
Group
.
DoesNotExist
):
r
everse
(
'dashboard.views.group-detail'
,
args
=
[
group_pk
])
)
r
aise
Http404
(
)
else
:
else
:
return
ret
default
=
None
val
=
super
(
UserCreationView
,
self
)
.
get_form_kwargs
()
val
.
update
({
'choices'
:
choices
,
'default'
:
default
})
return
val
class
ProfileView
(
LoginRequiredMixin
,
DetailView
):
class
ProfileView
(
LoginRequiredMixin
,
SuccessMessageMixin
,
UpdateView
):
template_name
=
"dashboard/profile.html"
template_name
=
"dashboard/profile.html"
model
=
User
model
=
User
slug_field
=
"username"
slug_field
=
"username"
slug_url_kwarg
=
"username"
slug_url_kwarg
=
"username"
form_class
=
UserEditForm
success_message
=
_
(
"Successfully modified user."
)
def
get
(
self
,
*
args
,
**
kwargs
):
def
get
(
self
,
*
args
,
**
kwargs
):
user
=
self
.
request
.
user
user
=
self
.
request
.
user
...
@@ -357,6 +373,15 @@ class ProfileView(LoginRequiredMixin, DetailView):
...
@@ -357,6 +373,15 @@ class ProfileView(LoginRequiredMixin, DetailView):
user
,
self
.
request
.
user
)
user
,
self
.
request
.
user
)
return
context
return
context
def
post
(
self
,
request
,
*
args
,
**
kwargs
):
if
not
request
.
user
.
has_perm
(
'auth.change_user'
):
raise
PermissionDenied
()
return
super
(
ProfileView
,
self
)
.
post
(
self
,
request
,
*
args
,
**
kwargs
)
def
get_success_url
(
self
):
return
reverse
(
'dashboard.views.profile'
,
kwargs
=
self
.
kwargs
)
@require_POST
@require_POST
def
toggle_use_gravatar
(
request
,
**
kwargs
):
def
toggle_use_gravatar
(
request
,
**
kwargs
):
...
@@ -480,3 +505,48 @@ class ConnectCommandCreate(LoginRequiredMixin, SuccessMessageMixin,
...
@@ -480,3 +505,48 @@ class ConnectCommandCreate(LoginRequiredMixin, SuccessMessageMixin,
kwargs
=
super
(
ConnectCommandCreate
,
self
)
.
get_form_kwargs
()
kwargs
=
super
(
ConnectCommandCreate
,
self
)
.
get_form_kwargs
()
kwargs
[
'user'
]
=
self
.
request
.
user
kwargs
[
'user'
]
=
self
.
request
.
user
return
kwargs
return
kwargs
class
UserList
(
LoginRequiredMixin
,
PermissionRequiredMixin
,
SingleTableView
):
template_name
=
"dashboard/user-list.html"
permission_required
=
"auth.change_user"
model
=
User
table_class
=
UserListTable
table_pagination
=
True
def
get_context_data
(
self
,
*
args
,
**
kwargs
):
context
=
super
(
UserList
,
self
)
.
get_context_data
(
*
args
,
**
kwargs
)
context
[
'search_form'
]
=
self
.
search_form
return
context
def
get
(
self
,
*
args
,
**
kwargs
):
self
.
search_form
=
UserListSearchForm
(
self
.
request
.
GET
)
self
.
search_form
.
full_clean
()
if
self
.
request
.
is_ajax
():
users
=
[
{
'url'
:
reverse
(
"dashboard.views.profile"
,
args
=
[
i
.
username
]),
'name'
:
i
.
get_full_name
()
or
i
.
username
,
'org_id'
:
i
.
profile
.
org_id
,
}
for
i
in
self
.
get_queryset
()]
return
HttpResponse
(
json
.
dumps
(
users
),
content_type
=
"application/json"
)
else
:
return
super
(
UserList
,
self
)
.
get
(
*
args
,
**
kwargs
)
def
get_queryset
(
self
):
logger
.
debug
(
'UserList.get_queryset() called. User:
%
s'
,
unicode
(
self
.
request
.
user
))
qs
=
User
.
objects
.
all
()
.
order_by
(
"-pk"
)
q
=
self
.
search_form
.
cleaned_data
.
get
(
's'
)
if
q
:
filters
=
(
Q
(
username__icontains
=
q
)
|
Q
(
email__icontains
=
q
)
|
Q
(
profile__org_id__icontains
=
q
))
for
w
in
q
.
split
()[:
3
]:
filters
|=
(
Q
(
first_name__icontains
=
w
)
|
Q
(
last_name__icontains
=
w
))
qs
=
qs
.
filter
(
filters
)
return
qs
.
select_related
(
"profile"
)
circle/dashboard/views/vm.py
View file @
1e514d5b
...
@@ -950,6 +950,7 @@ class VmList(LoginRequiredMixin, FilterMixin, ListView):
...
@@ -950,6 +950,7 @@ class VmList(LoginRequiredMixin, FilterMixin, ListView):
destroyed_at
=
None
)
.
all
()
destroyed_at
=
None
)
.
all
()
instances
=
[{
instances
=
[{
'pk'
:
i
.
pk
,
'pk'
:
i
.
pk
,
'url'
:
reverse
(
'dashboard.views.detail'
,
args
=
[
i
.
pk
]),
'name'
:
i
.
name
,
'name'
:
i
.
name
,
'icon'
:
i
.
get_status_icon
(),
'icon'
:
i
.
get_status_icon
(),
'host'
:
i
.
short_hostname
,
'host'
:
i
.
short_hostname
,
...
...
circle/locale/hu/LC_MESSAGES/django.po
View file @
1e514d5b
This source diff could not be displayed because it is too large. You can
view the blob
instead.
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