Show More
@@ -122,19 +122,15 b' def make_map(config):' | |||||
122 | action="show", conditions=dict(method=["GET"], |
|
122 | action="show", conditions=dict(method=["GET"], | |
123 | function=check_repo)) |
|
123 | function=check_repo)) | |
124 | #add repo perm member |
|
124 | #add repo perm member | |
125 |
m.connect('set_repo_perm_member', |
|
125 | m.connect('set_repo_perm_member', | |
126 | action="set_repo_perm_member", |
|
126 | "/repos/{repo_name:.*?}/grant_perm", | |
127 | conditions=dict(method=["POST"], function=check_repo)) |
|
127 | action="set_repo_perm_member", | |
|
128 | conditions=dict(method=["POST"], function=check_repo)) | |||
128 |
|
129 | |||
129 | #ajax delete repo perm user |
|
130 | #ajax delete repo perm user | |
130 | m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*?}", |
|
131 | m.connect('delete_repo_perm_member', | |
131 | action="delete_perm_user", |
|
132 | "/repos/{repo_name:.*?}/revoke_perm", | |
132 | conditions=dict(method=["DELETE"], function=check_repo)) |
|
133 | action="delete_repo_perm_member", | |
133 |
|
||||
134 | #ajax delete repo perm users_group |
|
|||
135 | m.connect('delete_repo_users_group', |
|
|||
136 | "/repos_delete_users_group/{repo_name:.*?}", |
|
|||
137 | action="delete_perm_users_group", |
|
|||
138 | conditions=dict(method=["DELETE"], function=check_repo)) |
|
134 | conditions=dict(method=["DELETE"], function=check_repo)) | |
139 |
|
135 | |||
140 | #settings actions |
|
136 | #settings actions | |
@@ -184,6 +180,18 b' def make_map(config):' | |||||
184 | m.connect("update_repos_group", "/repos_groups/{group_name:.*?}", |
|
180 | m.connect("update_repos_group", "/repos_groups/{group_name:.*?}", | |
185 | action="update", conditions=dict(method=["PUT"], |
|
181 | action="update", conditions=dict(method=["PUT"], | |
186 | function=check_group)) |
|
182 | function=check_group)) | |
|
183 | #add repo group perm member | |||
|
184 | m.connect('set_repo_group_perm_member', | |||
|
185 | "/repos_groups/{group_name:.*?}/grant_perm", | |||
|
186 | action="set_repo_group_perm_member", | |||
|
187 | conditions=dict(method=["POST"], function=check_group)) | |||
|
188 | ||||
|
189 | #ajax delete repo group perm | |||
|
190 | m.connect('delete_repo_group_perm_member', | |||
|
191 | "/repos_groups/{group_name:.*?}/revoke_perm", | |||
|
192 | action="delete_repo_group_perm_member", | |||
|
193 | conditions=dict(method=["DELETE"], function=check_group)) | |||
|
194 | ||||
187 | m.connect("delete_repos_group", "/repos_groups/{group_name:.*?}", |
|
195 | m.connect("delete_repos_group", "/repos_groups/{group_name:.*?}", | |
188 | action="delete", conditions=dict(method=["DELETE"], |
|
196 | action="delete", conditions=dict(method=["DELETE"], | |
189 | function=check_group_skip_path)) |
|
197 | function=check_group_skip_path)) | |
@@ -201,24 +209,6 b' def make_map(config):' | |||||
201 | action="show", conditions=dict(method=["GET"], |
|
209 | action="show", conditions=dict(method=["GET"], | |
202 | function=check_group)) |
|
210 | function=check_group)) | |
203 |
|
211 | |||
204 | #add repo perm member |
|
|||
205 | m.connect('set_repo_group_perm_member', |
|
|||
206 | "/set_repo_group_perm_member/{group_name:.*?}", |
|
|||
207 | action="set_repo_group_perm_member", |
|
|||
208 | conditions=dict(method=["POST"], function=check_group)) |
|
|||
209 |
|
||||
210 | # ajax delete repository group perm user |
|
|||
211 | m.connect('delete_repos_group_user_perm', |
|
|||
212 | "/delete_repos_group_user_perm/{group_name:.*?}", |
|
|||
213 | action="delete_repos_group_user_perm", |
|
|||
214 | conditions=dict(method=["DELETE"], function=check_group)) |
|
|||
215 |
|
||||
216 | # ajax delete repository group perm users_group |
|
|||
217 | m.connect('delete_repos_group_users_group_perm', |
|
|||
218 | "/delete_repos_group_users_group_perm/{group_name:.*?}", |
|
|||
219 | action="delete_repos_group_users_group_perm", |
|
|||
220 | conditions=dict(method=["DELETE"], function=check_group)) |
|
|||
221 |
|
||||
222 | #ADMIN USER REST ROUTES |
|
212 | #ADMIN USER REST ROUTES | |
223 | with rmap.submapper(path_prefix=ADMIN_PREFIX, |
|
213 | with rmap.submapper(path_prefix=ADMIN_PREFIX, | |
224 | controller='admin/users') as m: |
|
214 | controller='admin/users') as m: |
@@ -51,6 +51,7 b' from rhodecode.model.repo import RepoMod' | |||||
51 | from rhodecode.lib.compat import json |
|
51 | from rhodecode.lib.compat import json | |
52 | from sqlalchemy.sql.expression import func |
|
52 | from sqlalchemy.sql.expression import func | |
53 | from rhodecode.lib.exceptions import AttachedForksError |
|
53 | from rhodecode.lib.exceptions import AttachedForksError | |
|
54 | from rhodecode.lib.utils2 import safe_int | |||
54 |
|
55 | |||
55 | log = logging.getLogger(__name__) |
|
56 | log = logging.getLogger(__name__) | |
56 |
|
57 | |||
@@ -340,42 +341,33 b' class ReposController(BaseRepoController' | |||||
340 | return redirect(url('edit_repo', repo_name=repo_name)) |
|
341 | return redirect(url('edit_repo', repo_name=repo_name)) | |
341 |
|
342 | |||
342 | @HasRepoPermissionAllDecorator('repository.admin') |
|
343 | @HasRepoPermissionAllDecorator('repository.admin') | |
343 |
def delete_ |
|
344 | def delete_repo_perm_member(self, repo_name): | |
344 | """ |
|
345 | """ | |
345 | DELETE an existing repository permission user |
|
346 | DELETE an existing repository permission user | |
346 |
|
347 | |||
347 | :param repo_name: |
|
348 | :param repo_name: | |
348 | """ |
|
349 | """ | |
349 | try: |
|
350 | try: | |
350 | RepoModel().revoke_user_permission(repo=repo_name, |
|
351 | obj_type = request.POST.get('obj_type') | |
351 | user=request.POST['user_id']) |
|
352 | obj_id = None | |
|
353 | if obj_type == 'user': | |||
|
354 | obj_id = safe_int(request.POST.get('user_id')) | |||
|
355 | elif obj_type == 'user_group': | |||
|
356 | obj_id = safe_int(request.POST.get('user_group_id')) | |||
|
357 | ||||
|
358 | if obj_type == 'user': | |||
|
359 | RepoModel().revoke_user_permission(repo=repo_name, user=obj_id) | |||
|
360 | elif obj_type == 'user_group': | |||
|
361 | RepoModel().revoke_users_group_permission( | |||
|
362 | repo=repo_name, group_name=obj_id | |||
|
363 | ) | |||
352 | #TODO: implement this |
|
364 | #TODO: implement this | |
353 | #action_logger(self.rhodecode_user, 'admin_revoked_repo_permissions', |
|
365 | #action_logger(self.rhodecode_user, 'admin_revoked_repo_permissions', | |
354 | # repo_name, self.ip_addr, self.sa) |
|
366 | # repo_name, self.ip_addr, self.sa) | |
355 | Session().commit() |
|
367 | Session().commit() | |
356 | except Exception: |
|
368 | except Exception: | |
357 | log.error(traceback.format_exc()) |
|
369 | log.error(traceback.format_exc()) | |
358 |
h.flash(_('An error occurred during |
|
370 | h.flash(_('An error occurred during revoking of permission'), | |
359 | category='error') |
|
|||
360 | raise HTTPInternalServerError() |
|
|||
361 |
|
||||
362 | @HasRepoPermissionAllDecorator('repository.admin') |
|
|||
363 | def delete_perm_users_group(self, repo_name): |
|
|||
364 | """ |
|
|||
365 | DELETE an existing repository permission user group |
|
|||
366 |
|
||||
367 | :param repo_name: |
|
|||
368 | """ |
|
|||
369 |
|
||||
370 | try: |
|
|||
371 | RepoModel().revoke_users_group_permission( |
|
|||
372 | repo=repo_name, group_name=request.POST['users_group_id'] |
|
|||
373 | ) |
|
|||
374 | Session().commit() |
|
|||
375 | except Exception: |
|
|||
376 | log.error(traceback.format_exc()) |
|
|||
377 | h.flash(_('An error occurred during deletion of repository' |
|
|||
378 | ' user groups'), |
|
|||
379 | category='error') |
|
371 | category='error') | |
380 | raise HTTPInternalServerError() |
|
372 | raise HTTPInternalServerError() | |
381 |
|
373 |
@@ -294,49 +294,41 b' class ReposGroupsController(BaseControll' | |||||
294 | return redirect(url('edit_repos_group', group_name=group_name)) |
|
294 | return redirect(url('edit_repos_group', group_name=group_name)) | |
295 |
|
295 | |||
296 | @HasReposGroupPermissionAnyDecorator('group.admin') |
|
296 | @HasReposGroupPermissionAnyDecorator('group.admin') | |
297 |
def delete_repo |
|
297 | def delete_repo_group_perm_member(self, group_name): | |
298 | """ |
|
298 | """ | |
299 | DELETE an existing repository group permission user |
|
299 | DELETE an existing repository group permission user | |
300 |
|
300 | |||
301 | :param group_name: |
|
301 | :param group_name: | |
302 | """ |
|
302 | """ | |
303 | try: |
|
303 | try: | |
|
304 | obj_type = request.POST.get('obj_type') | |||
|
305 | obj_id = None | |||
|
306 | if obj_type == 'user': | |||
|
307 | obj_id = safe_int(request.POST.get('user_id')) | |||
|
308 | elif obj_type == 'user_group': | |||
|
309 | obj_id = safe_int(request.POST.get('user_group_id')) | |||
|
310 | ||||
304 | if not c.rhodecode_user.is_admin: |
|
311 | if not c.rhodecode_user.is_admin: | |
305 |
if c.rhodecode_user.user_id == |
|
312 | if obj_type == 'user' and c.rhodecode_user.user_id == obj_id: | |
306 | msg = _('Cannot revoke permission for yourself as admin') |
|
313 | msg = _('Cannot revoke permission for yourself as admin') | |
307 | h.flash(msg, category='warning') |
|
314 | h.flash(msg, category='warning') | |
308 | raise Exception('revoke admin permission on self') |
|
315 | raise Exception('revoke admin permission on self') | |
309 | recursive = str2bool(request.POST.get('recursive', False)) |
|
316 | recursive = str2bool(request.POST.get('recursive', False)) | |
310 | ReposGroupModel().delete_permission( |
|
317 | if obj_type == 'user': | |
311 | repos_group=group_name, obj=request.POST['user_id'], |
|
318 | ReposGroupModel().delete_permission( | |
312 | obj_type='user', recursive=recursive |
|
319 | repos_group=group_name, obj=obj_id, | |
313 | ) |
|
320 | obj_type='user', recursive=recursive | |
|
321 | ) | |||
|
322 | elif obj_type == 'user_group': | |||
|
323 | ReposGroupModel().delete_permission( | |||
|
324 | repos_group=group_name, obj=obj_id, | |||
|
325 | obj_type='users_group', recursive=recursive | |||
|
326 | ) | |||
|
327 | ||||
314 | Session().commit() |
|
328 | Session().commit() | |
315 | except Exception: |
|
329 | except Exception: | |
316 | log.error(traceback.format_exc()) |
|
330 | log.error(traceback.format_exc()) | |
317 |
h.flash(_('An error occurred during |
|
331 | h.flash(_('An error occurred during revoking of permission'), | |
318 | category='error') |
|
|||
319 | raise HTTPInternalServerError() |
|
|||
320 |
|
||||
321 | @HasReposGroupPermissionAnyDecorator('group.admin') |
|
|||
322 | def delete_repos_group_users_group_perm(self, group_name): |
|
|||
323 | """ |
|
|||
324 | DELETE an existing repository group permission user group |
|
|||
325 |
|
||||
326 | :param group_name: |
|
|||
327 | """ |
|
|||
328 |
|
||||
329 | try: |
|
|||
330 | recursive = str2bool(request.POST.get('recursive', False)) |
|
|||
331 | ReposGroupModel().delete_permission( |
|
|||
332 | repos_group=group_name, obj=request.POST['users_group_id'], |
|
|||
333 | obj_type='users_group', recursive=recursive |
|
|||
334 | ) |
|
|||
335 | Session().commit() |
|
|||
336 | except Exception: |
|
|||
337 | log.error(traceback.format_exc()) |
|
|||
338 | h.flash(_('An error occurred during deletion of group' |
|
|||
339 | ' user groups'), |
|
|||
340 | category='error') |
|
332 | category='error') | |
341 | raise HTTPInternalServerError() |
|
333 | raise HTTPInternalServerError() | |
342 |
|
334 |
@@ -295,7 +295,7 b' class UsersGroupsController(BaseControll' | |||||
295 | Session().commit() |
|
295 | Session().commit() | |
296 | except Exception: |
|
296 | except Exception: | |
297 | log.error(traceback.format_exc()) |
|
297 | log.error(traceback.format_exc()) | |
298 |
h.flash(_('An error occurred during |
|
298 | h.flash(_('An error occurred during revoking of permission'), | |
299 | category='error') |
|
299 | category='error') | |
300 | raise HTTPInternalServerError() |
|
300 | raise HTTPInternalServerError() | |
301 |
|
301 |
@@ -368,6 +368,20 b' var _run_callbacks = function(callbacks)' | |||||
368 | } |
|
368 | } | |
369 |
|
369 | |||
370 | /** |
|
370 | /** | |
|
371 | * turns objects into GET query string | |||
|
372 | */ | |||
|
373 | var toQueryString = function(o) { | |||
|
374 | if(typeof o !== 'object') { | |||
|
375 | return false; | |||
|
376 | } | |||
|
377 | var _p, _qs = []; | |||
|
378 | for(_p in o) { | |||
|
379 | _qs.push(encodeURIComponent(_p) + '=' + encodeURIComponent(o[_p])); | |||
|
380 | } | |||
|
381 | return _qs.join('&'); | |||
|
382 | }; | |||
|
383 | ||||
|
384 | /** | |||
371 | * Partial Ajax Implementation |
|
385 | * Partial Ajax Implementation | |
372 | * |
|
386 | * | |
373 | * @param url: defines url to make partial request |
|
387 | * @param url: defines url to make partial request | |
@@ -441,17 +455,6 b' var ajaxPOST = function(url,postData,suc' | |||||
441 | // Set special header for ajax == HTTP_X_PARTIAL_XHR |
|
455 | // Set special header for ajax == HTTP_X_PARTIAL_XHR | |
442 | YUC.initHeader('X-PARTIAL-XHR',true); |
|
456 | YUC.initHeader('X-PARTIAL-XHR',true); | |
443 |
|
457 | |||
444 | var toQueryString = function(o) { |
|
|||
445 | if(typeof o !== 'object') { |
|
|||
446 | return false; |
|
|||
447 | } |
|
|||
448 | var _p, _qs = []; |
|
|||
449 | for(_p in o) { |
|
|||
450 | _qs.push(encodeURIComponent(_p) + '=' + encodeURIComponent(o[_p])); |
|
|||
451 | } |
|
|||
452 | return _qs.join('&'); |
|
|||
453 | }; |
|
|||
454 |
|
||||
455 | var sUrl = url; |
|
458 | var sUrl = url; | |
456 | var callback = { |
|
459 | var callback = { | |
457 | success: success, |
|
460 | success: success, | |
@@ -2035,7 +2038,38 b' var addPermAction = function(_html, user' | |||||
2035 | YUD.insertAfter(el, last_node); |
|
2038 | YUD.insertAfter(el, last_node); | |
2036 | } |
|
2039 | } | |
2037 | } |
|
2040 | } | |
|
2041 | function ajaxActionRevokePermission(url, obj_id, obj_type, field_id, extra_data) { | |||
|
2042 | var callback = { | |||
|
2043 | success: function (o) { | |||
|
2044 | var tr = YUD.get(String(field_id)); | |||
|
2045 | tr.parentNode.removeChild(tr); | |||
|
2046 | }, | |||
|
2047 | failure: function (o) { | |||
|
2048 | alert(_TM['Failed to remoke permission'] + ": " + o.status); | |||
|
2049 | }, | |||
|
2050 | }; | |||
|
2051 | query_params = { | |||
|
2052 | '_method': 'delete' | |||
|
2053 | } | |||
|
2054 | // put extra data into POST | |||
|
2055 | if (extra_data !== undefined && (typeof extra_data === 'object')){ | |||
|
2056 | for(k in extra_data){ | |||
|
2057 | query_params[k] = extra_data[k]; | |||
|
2058 | } | |||
|
2059 | } | |||
2038 |
|
2060 | |||
|
2061 | if (obj_type=='user'){ | |||
|
2062 | query_params['user_id'] = obj_id; | |||
|
2063 | query_params['obj_type'] = 'user'; | |||
|
2064 | } | |||
|
2065 | else if (obj_type=='user_group'){ | |||
|
2066 | query_params['user_group_id'] = obj_id; | |||
|
2067 | query_params['obj_type'] = 'user_group'; | |||
|
2068 | } | |||
|
2069 | ||||
|
2070 | var request = YAHOO.util.Connect.asyncRequest('POST', url, callback, | |||
|
2071 | toQueryString(query_params)); | |||
|
2072 | }; | |||
2039 |
|
|
2073 | /* Multi selectors */ | |
2040 |
|
2074 | |||
2041 | var MultiSelectWidget = function(selected_id, available_id, form_id){ |
|
2075 | var MultiSelectWidget = function(selected_id, available_id, form_id){ |
@@ -87,29 +87,10 b'' | |||||
87 | </table> |
|
87 | </table> | |
88 | <script type="text/javascript"> |
|
88 | <script type="text/javascript"> | |
89 | function ajaxActionRevoke(obj_id, obj_type, field_id) { |
|
89 | function ajaxActionRevoke(obj_id, obj_type, field_id) { | |
90 | var callback = { |
|
90 | url = "${h.url('delete_repo_perm_member',repo_name=c.repo_name)}"; | |
91 | success: function (o) { |
|
91 | ajaxActionRevokePermission(url, obj_id, obj_type, field_id); | |
92 | var tr = YUD.get(String(field_id)); |
|
|||
93 | tr.parentNode.removeChild(tr); |
|
|||
94 | }, |
|
|||
95 | failure: function (o) { |
|
|||
96 | alert(_TM['Failed to remoke permission'] + ": " + o.status); |
|
|||
97 | }, |
|
|||
98 | }; |
|
|||
99 | if (obj_type=='user'){ |
|
|||
100 | var sUrl = "${h.url('delete_repo_user',repo_name=c.repo_name)}"; |
|
|||
101 | var postData = '_method=delete&user_id={0}&obj_type=user'.format(obj_id); |
|
|||
102 | } |
|
|||
103 | else if (obj_type=='user_group'){ |
|
|||
104 | var sUrl = "${h.url('delete_repo_users_group',repo_name=c.repo_name)}"; |
|
|||
105 | var postData = '_method=delete&users_group_id={0}&obj_type=user_group'.format(obj_id); |
|
|||
106 |
|
||||
107 | } |
|
|||
108 |
|
||||
109 | var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData); |
|
|||
110 | }; |
|
92 | }; | |
111 |
|
93 | |||
112 |
|
||||
113 | YUE.onDOMReady(function () { |
|
94 | YUE.onDOMReady(function () { | |
114 | if (!YUD.hasClass('perm_new_member_name', 'error')) { |
|
95 | if (!YUD.hasClass('perm_new_member_name', 'error')) { | |
115 | YUD.setStyle('add_perm_input', 'display', 'none'); |
|
96 | YUD.setStyle('add_perm_input', 'display', 'none'); |
@@ -90,29 +90,10 b'' | |||||
90 | </table> |
|
90 | </table> | |
91 | <script type="text/javascript"> |
|
91 | <script type="text/javascript"> | |
92 | function ajaxActionRevoke(obj_id, obj_type, field_id) { |
|
92 | function ajaxActionRevoke(obj_id, obj_type, field_id) { | |
93 | var callback = { |
|
93 | url = "${h.url('delete_repo_group_perm_member', group_name=c.repos_group.group_name)}"; | |
94 | success: function (o) { |
|
94 | ajaxActionRevokePermission(url, obj_id, obj_type, field_id, {recursive:YUD.get('recursive').checked}); | |
95 | var tr = YUD.get(String(field_id)); |
|
|||
96 | tr.parentNode.removeChild(tr); |
|
|||
97 | }, |
|
|||
98 | failure: function (o) { |
|
|||
99 | alert(_TM['Failed to remoke permission'] + ": " + o.status); |
|
|||
100 | }, |
|
|||
101 | }; |
|
|||
102 | var recursive = YUD.get('recursive').checked; |
|
|||
103 |
|
||||
104 | if (obj_type=='user'){ |
|
|||
105 | var sUrl = "${h.url('delete_repos_group_user_perm',group_name=c.repos_group.group_name)}"; |
|
|||
106 | var postData = '_method=delete&recursive={0}&user_id={1}&obj_type=user'.format(recursive,obj_id); |
|
|||
107 | } |
|
|||
108 | else if (obj_type=='user_group'){ |
|
|||
109 | var sUrl = "${h.url('delete_repos_group_users_group_perm',group_name=c.repos_group.group_name)}"; |
|
|||
110 | var postData = '_method=delete&recursive={0}&users_group_id={0}&obj_type=user_group'.format(recursive,obj_id); |
|
|||
111 | } |
|
|||
112 | var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData); |
|
|||
113 | }; |
|
95 | }; | |
114 |
|
96 | |||
115 |
|
||||
116 | YUE.onDOMReady(function () { |
|
97 | YUE.onDOMReady(function () { | |
117 | if (!YUD.hasClass('perm_new_member_name', 'error')) { |
|
98 | if (!YUD.hasClass('perm_new_member_name', 'error')) { | |
118 | YUD.setStyle('add_perm_input', 'display', 'none'); |
|
99 | YUD.setStyle('add_perm_input', 'display', 'none'); |
@@ -67,27 +67,10 b'' | |||||
67 | </table> |
|
67 | </table> | |
68 | <script type="text/javascript"> |
|
68 | <script type="text/javascript"> | |
69 | function ajaxActionRevoke(obj_id, obj_type, field_id) { |
|
69 | function ajaxActionRevoke(obj_id, obj_type, field_id) { | |
70 | var callback = { |
|
70 | url = "${h.url('delete_user_group_perm_member', id=c.users_group.users_group_id)}"; | |
71 | success: function (o) { |
|
71 | ajaxActionRevokePermission(url, obj_id, obj_type, field_id); | |
72 | var tr = YUD.get(String(field_id)); |
|
|||
73 | tr.parentNode.removeChild(tr); |
|
|||
74 | }, |
|
|||
75 | failure: function (o) { |
|
|||
76 | alert(_TM['Failed to remoke permission'] + ": " + o.status); |
|
|||
77 | }, |
|
|||
78 | }; |
|
|||
79 | var sUrl = "${h.url('delete_user_group_perm_member', id=c.users_group.users_group_id)}"; |
|
|||
80 | if (obj_type=='user'){ |
|
|||
81 | var postData = '_method=delete&user_id={0}&obj_type=user'.format(obj_id); |
|
|||
82 | } |
|
|||
83 | else if (obj_type=='user_group'){ |
|
|||
84 | var postData = '_method=delete&user_group_id={0}&obj_type=user_group'.format(obj_id); |
|
|||
85 | } |
|
|||
86 |
|
||||
87 | var request = YAHOO.util.Connect.asyncRequest('POST', sUrl, callback, postData); |
|
|||
88 | }; |
|
72 | }; | |
89 |
|
73 | |||
90 |
|
||||
91 | YUE.onDOMReady(function () { |
|
74 | YUE.onDOMReady(function () { | |
92 | if (!YUD.hasClass('perm_new_member_name', 'error')) { |
|
75 | if (!YUD.hasClass('perm_new_member_name', 'error')) { | |
93 | YUD.setStyle('add_perm_input', 'display', 'none'); |
|
76 | YUD.setStyle('add_perm_input', 'display', 'none'); |
General Comments 0
You need to be logged in to leave comments.
Login now