Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
J
jumpserver
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
ops
jumpserver
Commits
2ec0ab87
Commit
2ec0ab87
authored
8 years ago
by
ibuler
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix form bug
parent
695e4da8
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
82 additions
and
229 deletions
+82
-229
api.py
apps/assets/api.py
+16
-0
forms.py
apps/assets/forms.py
+1
-1
user.py
apps/assets/models/user.py
+1
-0
asset_group_detail.html
apps/assets/templates/assets/asset_group_detail.html
+48
-112
api_urls.py
apps/assets/urls/api_urls.py
+3
-0
models.py
apps/ops/models.py
+4
-1
task_detail.html
apps/ops/templates/ops/task_detail.html
+5
-5
asset_permission_create_update.html
...perms/templates/perms/asset_permission_create_update.html
+2
-2
forms.py
apps/users/forms.py
+1
-1
_user_bulk_update_modal.html
apps/users/templates/users/_user_bulk_update_modal.html
+0
-72
user_list.html
apps/users/templates/users/user_list.html
+0
-35
requirements.txt
requirements/requirements.txt
+1
-0
No files found.
apps/assets/api.py
View file @
2ec0ab87
...
...
@@ -172,3 +172,19 @@ class AssetAdminUserTestView(AssetRefreshHardwareView):
return
Response
(
'1'
)
else
:
return
Response
(
'0'
,
status
=
502
)
class
AssetGroupPushSystemUserView
(
generics
.
UpdateAPIView
):
queryset
=
AssetGroup
.
objects
.
all
()
permission_classes
=
(
IsSuperUser
,)
def
patch
(
self
,
request
,
*
args
,
**
kwargs
):
asset_group
=
self
.
get_object
()
assets
=
asset_group
.
assets
.
all
()
system_user_id
=
self
.
request
.
data
[
'system_user'
]
system_user
=
get_object_or_none
(
SystemUser
,
id
=
system_user_id
)
if
not
assets
or
not
system_user
:
return
Response
(
'Invalid system user id or asset group id'
,
status
=
404
)
task
=
push_users
.
delay
([
asset
.
_to_secret_json
()
for
asset
in
assets
],
system_user
.
_to_secret_json
())
return
Response
(
task
.
id
)
This diff is collapsed.
Click to expand it.
apps/assets/forms.py
View file @
2ec0ab87
...
...
@@ -70,7 +70,7 @@ class AssetBulkUpdateForm(forms.ModelForm):
required
=
True
,
help_text
=
'* required'
,
label
=
_
(
'Select assets'
),
choices
=
[(
asset
.
id
,
asset
.
hostname
)
for
asset
in
Asset
.
objects
.
all
()],
#
choices=[(asset.id, asset.hostname) for asset in Asset.objects.all()],
widget
=
forms
.
SelectMultiple
(
attrs
=
{
'class'
:
'select2'
,
...
...
This diff is collapsed.
Click to expand it.
apps/assets/models/user.py
View file @
2ec0ab87
...
...
@@ -157,6 +157,7 @@ class SystemUser(models.Model):
def
__unicode__
(
self
):
return
self
.
name
__str__
=
__unicode__
@property
def
password
(
self
):
...
...
This diff is collapsed.
Click to expand it.
apps/assets/templates/assets/asset_group_detail.html
View file @
2ec0ab87
...
...
@@ -51,22 +51,6 @@
</tr>
</thead>
<tbody>
{# {% for asset in assets %}#}
{#
<tr
id=
"bdg_asset"
data-aid=
"{{ asset.id }}"
>
#}
{#
<td>
{{ asset.hostname }}
</td>
#}
{#
<td>
{{ asset.ip }}
</td>
#}
{#
<td>
{{ asset.port }}
</td>
#}
{# {% if asset.is_active %}#}
{#
<td><i
class=
"fa fa-circle text-navy"
></i></td>
#}
{# {% else %}#}
{#
<td><i
class=
"fa fa-circle text-danger"
></i></td>
#}
{# {% endif %}#}
{#
<td>
#}
{#
<a
class=
"btn btn-xs btn-danger m-l-xs btn_asset_delete"
data-aid=
"{{ asset.id }}"
>
{% trans "Delete" %}
</a>
#}
{#
<a
class=
"btn btn-xs btn-info m-l-xs btn_asset_update"
data-aid=
"{{ asset.id }}"
href=
"{% url 'assets:asset-update' pk=asset.id %}"
>
{% trans "Update" %}
</a>
#}
{#
</td>
#}
{#
</tr>
#}
{# {% endfor %}#}
</tbody>
</table>
</div>
...
...
@@ -83,9 +67,9 @@
<form>
<tr
class=
"no-borders-tr"
>
<td
colspan=
"2"
>
<select
data-placeholder=
"{% trans 'Select assets' %}"
class=
"select2"
style=
"width: 100%"
multiple=
""
tabindex=
"4"
>
<select
data-placeholder=
"{% trans 'Select assets' %}"
class=
"select2
asset-select
"
style=
"width: 100%"
multiple=
""
tabindex=
"4"
>
{% for asset in assets_remain %}
<option
value=
"{{ asset.id }}"
id=
"opt_{{ asset.id }}"
>
{{ asset.
ip }}:{{ asset.port
}}
</option>
<option
value=
"{{ asset.id }}"
id=
"opt_{{ asset.id }}"
>
{{ asset.
hostname
}}
</option>
{% endfor %}
</select>
</td>
...
...
@@ -100,20 +84,47 @@
</table>
</div>
</div>
</div>
<div
class=
"col-sm-5"
style=
"padding-left: 0;padding-right: 0"
>
<div
class=
"panel panel-warning"
>
<div
class=
"panel-heading"
>
<i
class=
"fa fa-info-circle"
></i>
{% trans 'Push system user to this group assets' %}
</div>
<div
class=
"panel-body"
>
<table
class=
"table"
>
<tbody>
<form>
<tr
class=
"no-borders-tr"
>
<td
colspan=
"2"
>
<select
data-placeholder=
"{% trans 'Select system users' %}"
class=
"select2 system-user-select"
style=
"width: 100%"
multiple=
""
tabindex=
"4"
>
{% for system_user in system_users %}
<option
value=
"{{ system_user.id }}"
>
{{ system_user.name }}
</option>
{% endfor %}
</select>
</td>
</tr>
<tr
class=
"no-borders-tr"
>
<td
colspan=
"2"
>
<button
type=
"button"
class=
"btn btn-warning btn-sm btn-push-system-user"
>
{% trans 'Push' %}
</button>
</td>
</tr>
</form>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{#
</div>
#}
{% endblock %}
{% block custom_foot_js %}
<script>
jumpserver
.
assets_selected
=
{};
jumpserver
.
system_users_selected
=
{};
function
updateAssetsGroup
(
assets
)
{
var
the_url
=
"{% url 'api-assets:asset-groups-update' pk=asset_group.id %}"
;
var
body
=
{
...
...
@@ -154,7 +165,7 @@ function updateAssetsGroup(assets) {
});
}
function
objectDelete
(
obj
,
name
,
url
,
data
)
{
function
leaveGroup
(
obj
,
name
,
url
,
data
)
{
function
doDelete
()
{
var
body
=
data
;
var
success
=
function
()
{
...
...
@@ -186,27 +197,18 @@ function objectDelete(obj, name, url, data) {
});
}
function
updateAssetGroupSystemUsers
(
system_users
)
{
var
the_url
=
"{% url 'api-assets:asset-group
s-update-systemusers
' pk=asset_group.id %}"
;
function
pushSystemUser
(
sysUserID
)
{
var
the_url
=
"{% url 'api-assets:asset-group
-push-system-user
' pk=asset_group.id %}"
;
var
body
=
{
system_user
s
:
Object
.
assign
([],
system_users
)
system_user
:
sysUserID
};
var
success
=
function
(
data
)
{
$
(
'.select2-selection__rendered'
).
empty
();
$
(
'#groups_selected'
).
val
(
''
);
$
.
map
(
jumpserver
.
system_users_selected
,
function
(
system_user
,
index
)
{
$
(
'#opt_'
+
index
).
remove
();
$
(
'.system-user-table tbody'
).
append
(
'<tr>'
+
'<td><b class="bdg_group" data-sid="'
+
index
+
'">'
+
system_user
+
'</b></td>'
+
'<td><button class="btn btn-danger btn-xs pull-right btn_leave_group" type="button"><i class="fa fa-minus"></i></button></td>'
+
'</tr>'
)
});
jumpserver
.
system_users_selected
=
{};
var
url
=
"{% url 'ops:task-detail' pk=234234234 %}"
.
replace
(
"234234234"
,
data
);
location
.
href
=
url
};
APIUpdateAttr
({
url
:
the_url
,
method
:
'PATCH'
,
body
:
JSON
.
stringify
(
body
),
success
:
success
});
...
...
@@ -232,17 +234,7 @@ Array.prototype.unique = function(){
};
$
(
document
).
ready
(
function
()
{
$
(
'.select2'
).
select2
()
.
on
(
"select2:select"
,
function
(
evt
)
{
var
data
=
evt
.
params
.
data
;
jumpserver
.
assets_selected
[
data
.
id
]
=
data
.
text
;
jumpserver
.
system_users_selected
[
data
.
id
]
=
data
.
text
;
})
.
on
(
'select2:unselect'
,
function
(
evt
)
{
var
data
=
evt
.
params
.
data
;
delete
jumpserver
.
assets_selected
[
data
.
id
];
delete
jumpserver
.
system_users_selected
[
data
.
id
]
});
$
(
'.select2'
).
select2
();
var
options
=
{
ele
:
$
(
'#asset_list_table'
),
...
...
@@ -274,39 +266,13 @@ $(document).ready(function () {
jumpserver
.
initDataTable
(
options
);
})
.
on
(
'click'
,
'.btn-asset-add-groups'
,
function
()
{
if
(
Object
.
keys
(
jumpserver
.
assets_selected
).
length
===
0
)
{
return
false
;
}
jumpserver
.
system_users_selected
=
{};
var
$data_table
=
$
(
"#asset_list_table"
).
DataTable
();
var
assets
=
[];
$
.
ajax
({
url
:
'{% url "api-assets:asset-list" %}?asset_group_id={{ asset_group.id }}'
,
method
:
'GET'
,
dataType
:
'json'
,
success
:
function
(
result
)
{
for
(
var
i
in
result
){
if
(
!
isNaN
(
parseInt
(
result
[
i
][
'id'
])))
{
assets
.
push
(
parseInt
(
result
[
i
][
'id'
]))
}
}
$
.
map
(
jumpserver
.
assets_selected
,
function
(
value
,
index
)
{
assets
.
push
(
parseInt
(
index
));
});
assets
.
unique
();
var
the_url
=
"{% url 'api-assets:asset-groups-update' pk=asset_group.id %}"
;
var
body
=
{
"assets"
:
assets
};
APIUpdateAttr
({
url
:
the_url
,
body
:
JSON
.
stringify
(
body
),
method
:
'PATCH'
});
{
#
TODO
:
reflash
the
table
and
reset
the
jumpserver
variables
#
}
{
#
window
.
location
.
href
=
'{% url "assets:asset-group-detail" pk=asset_group.id %}'
;
#
}
}
});
.
on
(
'click'
,
'.btn-push-system-user'
,
function
()
{
var
data
=
$
(
'.system-user-select'
).
select2
();
var
system_id
=
data
.
val
()[
0
];
if
(
!
system_id
)
{
return
false
}
pushSystemUser
(
system_id
)
})
.
on
(
'click'
,
'.btn_asset_delete'
,
function
()
{
...
...
@@ -320,40 +286,10 @@ $(document).ready(function () {
var
delete_asset_id
=
$
(
this
).
data
(
'aid'
);
assets
.
remove
(
delete_asset_id
);
var
data
=
{
"assets"
:
assets
};
objectDelete
(
$this
,
name
,
the_url
,
data
);
leaveGroup
(
$this
,
name
,
the_url
,
data
);
})
.
on
(
'click'
,
'.btn-asset-add-groups-system-users'
,
function
()
{
if
(
Object
.
keys
(
jumpserver
.
system_users_selected
).
length
===
0
)
{
return
false
;
}
jumpserver
.
assets_selected
=
{};
var
system_users
=
$
(
'.bdg_system_user'
).
map
(
function
()
{
return
$
(
this
).
data
(
'sid'
);
}).
get
();
$
.
map
(
jumpserver
.
system_users_selected
,
function
(
value
,
index
)
{
system_users
.
push
(
parseInt
(
index
));
$
(
'#opt_'
+
index
).
remove
();
});
system_users
.
unique
();
updateAssetGroupSystemUsers
(
system_users
);
})
.
on
(
'click'
,
'.btn_leave_asset_group'
,
function
()
{
var
$this
=
$
(
this
);
var
$tr
=
$this
.
closest
(
'tr'
);
var
$badge
=
$tr
.
find
(
'.bdg_system_user'
);
var
sid
=
$badge
.
data
(
'sid'
);
var
name
=
$badge
.
html
()
||
$badge
.
text
();
$
(
'system-user-table'
).
append
(
'<option value="'
+
sid
+
'" id="opt_'
+
sid
+
'">'
+
name
+
'</option>'
);
$tr
.
remove
();
var
system_users
=
$
(
'.bdg_system_user'
).
map
(
function
()
{
return
$
(
this
).
data
(
'sid'
);
}).
get
();
updateAssetGroupSystemUsers
(
system_users
)
})
</script>
{% endblock %}
This diff is collapsed.
Click to expand it.
apps/assets/urls/api_urls.py
View file @
2ec0ab87
...
...
@@ -29,6 +29,9 @@ urlpatterns = [
url
(
r'^v1/assets/(?P<pk>\d+)/system-users/$'
,
api
.
SystemUserUpdateApi
.
as_view
(),
name
=
'asset-update-system-users'
),
url
(
r'^v1/asset-groups/(?P<pk>\d+)/push-system-user/$'
,
api
.
AssetGroupPushSystemUserView
.
as_view
(),
name
=
'asset-group-push-system-user'
),
# update the system users, which add and delete the asset to the system user
url
(
r'^v1/system_user/(?P<pk>\d+)/assets/$'
,
api
.
SystemUserUpdateAssetsApi
.
as_view
(),
name
=
'systemuser-update-assets'
),
...
...
This diff is collapsed.
Click to expand it.
apps/ops/models.py
View file @
2ec0ab87
...
...
@@ -7,6 +7,7 @@ import json
from
django.db
import
models
from
django.utils.translation
import
ugettext_lazy
as
_
from
assets.models
import
Asset
__all__
=
[
"Task"
]
...
...
@@ -33,7 +34,9 @@ class Task(models.Model):
@property
def
total_assets
(
self
):
return
self
.
assets
.
split
(
','
)
assets_id
=
[
i
for
i
in
self
.
assets
.
split
(
','
)
if
i
.
isdigit
()]
assets
=
Asset
.
objects
.
filter
(
id__in
=
assets_id
)
return
assets
@property
def
assets_json
(
self
):
...
...
This diff is collapsed.
Click to expand it.
apps/ops/templates/ops/task_detail.html
View file @
2ec0ab87
...
...
@@ -83,11 +83,11 @@
<tr>
<td>
{% trans 'assets' %}:
</td>
<td>
<b>
{% for asset in object.total_assets %}
{{ asset
}}
<br/>
{% endfor %}
</b>
<b>
{% for asset in object.total_assets %}
{{ asset.hostname
}}
<br/>
{% endfor %}
</b>
</td>
</tr>
</tbody>
...
...
This diff is collapsed.
Click to expand it.
apps/perms/templates/perms/asset_permission_create_update.html
View file @
2ec0ab87
...
...
@@ -38,9 +38,9 @@
{% bootstrap_field form.user_groups layout="horizontal" %}
<div
class=
"hr-line-dashed"
></div>
<h3>
{% trans 'Asset' %}
</h3>
{
{ form.assets|bootstrap_horizontal|safe }
}
{
% bootstrap_field form.assets layout="horizontal" %
}
{% bootstrap_field form.asset_groups layout="horizontal" %}
{
{ form.system_users |bootstrap_horizontal }
}
{
% bootstrap_field form.system_users layout="horizontal" %
}
<div
class=
"hr-line-dashed"
></div>
<h3>
{% trans 'Other' %}
</h3>
<div
class=
"form-group"
>
...
...
This diff is collapsed.
Click to expand it.
apps/users/forms.py
View file @
2ec0ab87
...
...
@@ -123,7 +123,7 @@ class UserBulkUpdateForm(forms.ModelForm):
required
=
True
,
help_text
=
'* required'
,
label
=
_
(
'Select users'
),
choices
=
[(
user
.
id
,
user
.
name
)
for
user
in
User
.
objects
.
all
()],
# choices=[(user.id, user.name
) for user in User.objects.all()],
widget
=
forms
.
SelectMultiple
(
attrs
=
{
'class'
:
'select2'
,
...
...
This diff is collapsed.
Click to expand it.
apps/users/templates/users/_user_bulk_update_modal.html
deleted
100644 → 0
View file @
695e4da8
{% extends '_modal.html' %}
{% load i18n %}
{% load bootstrap3 %}
{% block modal_id %}user_bulk_update_modal{% endblock %}
{% block modal_class %}modal-lg{% endblock %}
{% block modal_title%}{% trans "Update selected user" %}{% endblock %}
{% block modal_body %}
{% block form %}
<div
class=
"ydxbd"
id=
"formlists"
style=
"display: block;"
>
<p
id=
"tags_p"
class=
"mgl-5 c02"
>
选择需要修改属性
</p>
<div
class=
"tagBtnList"
>
<a
class=
"label label-primary"
id=
"change_all"
value=
"1"
>
全选
</a>
{% for field in form %}
{# {% if field.name != 'assets' %}#}
<a
data-id=
"{{ field.id_for_label }}"
class=
"label label-default label-primary field-tag"
value=
"1"
>
{{ field.label }}
</a>
{# {% endif %}#}
{% endfor %}
</div>
</div>
<form
method=
"post"
class=
"form-horizontal"
id=
"add_form"
>
{% csrf_token %}
{% bootstrap_form form layout="horizontal" %}
<div
class=
"form-group abc"
>
<div
class=
"col-sm-4 col-sm-offset-2"
>
<button
class=
"btn btn-white"
type=
"reset"
>
{% trans 'Reset' %}
</button>
<button
class=
"btn btn-primary"
type=
"submit"
>
{% trans 'Submit' %}
</button>
</div>
</div>
</form>
{% endblock %}
{% endblock %}
{#{% block custom_foot_js %}#}
{#
<script>
#
}
{
#
$
(
document
).
ready
(
function
()
{
#
}
{
#
$
(
'.select2'
).
select2
();
#
}
{
#
}).
on
(
'click'
,
'.field-tag'
,
function
()
{
#
}
{
#
changeField
(
this
);
#
}
{
#
}).
on
(
'click'
,
'#change_all'
,
function
()
{
#
}
{
#
var
tag_fields
=
$
(
'.field-tag'
);
#
}
{
#
var
$this
=
$
(
this
);
#
}
{
#
var
active
=
'1'
;
#
}
{
#
if
(
$this
.
attr
(
'value'
)
==
'0'
){
#
}
{
#
active
=
'0'
;
#
}
{
#
$this
.
attr
(
'value'
,
'1'
).
addClass
(
'label-primary'
)
#
}
{
#
}
else
{
#
}
{
#
active
=
'1'
;
#
}
{
#
$this
.
attr
(
'value'
,
'0'
).
removeClass
(
'label-primary'
)
#
}
{
#
}
#
}
{
#
$
.
each
(
tag_fields
,
function
(
k
,
v
)
{
#
}
{
#
changeField
(
v
,
active
)
#
}
{
#
})
#
}
{
#
});
#
}
{
##
}
{
#
function
changeField
(
obj
,
active
)
{
#
}
{
#
var
$this
=
$
(
obj
);
#
}
{
#
var
field_id
=
$this
.
data
(
'id'
);
#
}
{
#
if
(
!
active
)
{
#
}
{
#
active
=
$this
.
attr
(
'value'
);
#
}
{
#
}
#
}
{
#
if
(
active
==
'0'
)
{
#
}
{
#
$this
.
attr
(
'value'
,
'1'
).
addClass
(
'label-primary'
);
#
}
{
#
var
form_groups
=
$
(
'#add_form .form-group:not(.abc)'
);
#
}
{
#
form_groups
.
filter
(
':has(#'
+
field_id
+
')'
).
show
().
find
(
'select,input'
).
prop
(
'disabled'
,
false
)
#
}
{
#
}
else
{
#
}
{
#
$this
.
attr
(
'value'
,
'0'
).
removeClass
(
'label-primary'
);
#
}
{
#
var
form_groups
=
$
(
'#add_form .form-group:not(.abc)'
);
#
}
{
#
form_groups
.
filter
(
':has(#'
+
field_id
+
')'
).
hide
().
find
(
'select,input'
).
prop
(
'disabled'
,
true
)
#
}
{
#
}
#
}
{
#
}
#
}
{
#
</script>
#}
{#{% endblock %}#}
This diff is collapsed.
Click to expand it.
apps/users/templates/users/user_list.html
View file @
2ec0ab87
...
...
@@ -48,7 +48,6 @@
</div>
</div>
</div>
{% include "users/_user_bulk_update_modal.html" %}
{% include "users/_user_import_modal.html" %}
{% endblock %}
{% block content_bottom_left %}{% endblock %}
...
...
@@ -235,40 +234,6 @@ $(document).ready(function(){
var
uid
=
$this
.
data
(
'uid'
);
var
the_url
=
'{% url "api-users:user-detail" pk=99991937 %}'
.
replace
(
'99991937'
,
uid
);
objectDelete
(
$this
,
name
,
the_url
);
}).
on
(
'click'
,
'#btn_user_bulk_update'
,
function
(){
var
json_data
=
$
(
'#fm_user_bulk_update'
).
serializeObject
();
var
body
=
{};
body
.
enable_otp
=
(
json_data
.
enable_otp
===
'on'
)?
true
:
false
;
if
(
json_data
.
role
!=
''
)
{
body
.
role
=
json_data
.
role
;
}
if
(
json_data
.
groups
!=
undefined
)
{
body
.
groups
=
json_data
.
groups
;
}
if
(
typeof
body
.
groups
===
'string'
)
{
body
.
groups
=
[
parseInt
(
body
.
groups
)]
}
else
if
(
typeof
body
.
groups
===
'array'
)
{
var
new_groups
=
body
.
groups
.
map
(
Number
);
body
.
groups
=
new_groups
;
}
var
$data_table
=
$
(
'#user_list_table'
).
DataTable
();
var
post_list
=
[];
$data_table
.
rows
({
selected
:
true
}).
every
(
function
(){
var
content
=
Object
.
assign
({
id
:
this
.
data
().
id
},
body
);
post_list
.
push
(
content
);
});
if
(
post_list
===
[])
{
return
false
}
var
the_url
=
"{% url 'api-users:user-list' %}"
;
var
success
=
function
()
{
var
msg
=
"{% trans 'The selected users has been updated successfully.' %}"
;
swal
(
"{% trans 'User Updated' %}"
,
msg
,
"success"
);
$
(
'#user_list_table'
).
DataTable
().
ajax
.
reload
();
jumpserver
.
checked
=
false
;
};
{
#
APIUpdateAttr
({
url
:
the_url
,
method
:
'PATCH'
,
body
:
JSON
.
stringify
(
post_list
),
success
:
success
});
#
}
$
(
'#user_bulk_update_modal'
).
modal
(
'hide'
);
})
</script>
{% endblock %}
...
...
This diff is collapsed.
Click to expand it.
requirements/requirements.txt
View file @
2ec0ab87
...
...
@@ -17,3 +17,4 @@ itsdangerous
eventlet
django-filter
passlib
ForgeryPy
This diff is collapsed.
Click to expand it.
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