mirror of
https://github.com/ansible/awx.git
synced 2026-05-20 23:37:39 -02:30
Internationalized workflow related forms
This commit is contained in:
@@ -10,10 +10,16 @@
|
|||||||
* @description This form is for adding/editing a Job Template
|
* @description This form is for adding/editing a Job Template
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// export default
|
||||||
|
// angular.module('WorkflowMakerFormDefinition', [])
|
||||||
|
//
|
||||||
|
// .value ('WorkflowMakerFormObject', {
|
||||||
|
|
||||||
export default
|
export default
|
||||||
angular.module('WorkflowMakerFormDefinition', [])
|
angular.module('WorkflowMakerFormDefinition', [])
|
||||||
|
|
||||||
.value ('WorkflowMakerFormObject', {
|
.factory('WorkflowMakerFormObject', ['i18n', function(i18n) {
|
||||||
|
return {
|
||||||
|
|
||||||
addTitle: '',
|
addTitle: '',
|
||||||
editTitle: '',
|
editTitle: '',
|
||||||
@@ -25,23 +31,23 @@ export default
|
|||||||
|
|
||||||
fields: {
|
fields: {
|
||||||
edgeType: {
|
edgeType: {
|
||||||
label: 'Type',
|
label: i18n._('Type'),
|
||||||
type: 'radio_group',
|
type: 'radio_group',
|
||||||
ngShow: 'selectedTemplate && showTypeOptions',
|
ngShow: 'selectedTemplate && showTypeOptions',
|
||||||
ngDisabled: '!canAddWorkflowJobTemplate',
|
ngDisabled: '!canAddWorkflowJobTemplate',
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
label: 'On Success',
|
label: i18n._('On Success'),
|
||||||
value: 'success',
|
value: 'success',
|
||||||
ngShow: '!edgeTypeRestriction || edgeTypeRestriction === "successFailure"'
|
ngShow: '!edgeTypeRestriction || edgeTypeRestriction === "successFailure"'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'On Failure',
|
label: i18n._('On Failure'),
|
||||||
value: 'failure',
|
value: 'failure',
|
||||||
ngShow: '!edgeTypeRestriction || edgeTypeRestriction === "successFailure"'
|
ngShow: '!edgeTypeRestriction || edgeTypeRestriction === "successFailure"'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Always',
|
label: i18n._('Always'),
|
||||||
value: 'always',
|
value: 'always',
|
||||||
ngShow: '!edgeTypeRestriction || edgeTypeRestriction === "always"'
|
ngShow: '!edgeTypeRestriction || edgeTypeRestriction === "always"'
|
||||||
}
|
}
|
||||||
@@ -51,16 +57,16 @@ export default
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
credential: {
|
credential: {
|
||||||
label: 'Credential',
|
label: i18n._('Credential'),
|
||||||
type: 'lookup',
|
type: 'lookup',
|
||||||
sourceModel: 'credential',
|
sourceModel: 'credential',
|
||||||
sourceField: 'name',
|
sourceField: 'name',
|
||||||
ngClick: 'lookUpCredential()',
|
ngClick: 'lookUpCredential()',
|
||||||
requiredErrorMsg: "Please select a Credential.",
|
requiredErrorMsg: i18n._("Please select a Credential."),
|
||||||
class: 'Form-formGroup--fullWidth',
|
class: 'Form-formGroup--fullWidth',
|
||||||
awPopOver: "<p>Select the credential you want the job to use when accessing the remote hosts. Choose the credential containing " +
|
awPopOver: i18n._("<p>Select the credential you want the job to use when accessing the remote hosts. Choose the credential containing " +
|
||||||
" the username and SSH key or password that Ansible will need to log into the remote hosts.</p>",
|
" the username and SSH key or password that Ansible will need to log into the remote hosts.</p>"),
|
||||||
dataTitle: 'Credential',
|
dataTitle: i18n._('Credential'),
|
||||||
dataPlacement: 'right',
|
dataPlacement: 'right',
|
||||||
dataContainer: "body",
|
dataContainer: "body",
|
||||||
ngShow: "selectedTemplate.ask_credential_on_launch",
|
ngShow: "selectedTemplate.ask_credential_on_launch",
|
||||||
@@ -70,17 +76,17 @@ export default
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
inventory: {
|
inventory: {
|
||||||
label: 'Inventory',
|
label: i18n._('Inventory'),
|
||||||
type: 'lookup',
|
type: 'lookup',
|
||||||
sourceModel: 'inventory',
|
sourceModel: 'inventory',
|
||||||
sourceField: 'name',
|
sourceField: 'name',
|
||||||
list: 'OrganizationList',
|
list: 'OrganizationList',
|
||||||
basePath: 'organization',
|
basePath: 'organization',
|
||||||
ngClick: 'lookUpInventory()',
|
ngClick: 'lookUpInventory()',
|
||||||
requiredErrorMsg: "Please select an Inventory.",
|
requiredErrorMsg: i18n._("Please select an Inventory."),
|
||||||
class: 'Form-formGroup--fullWidth',
|
class: 'Form-formGroup--fullWidth',
|
||||||
awPopOver: "<p>Select the inventory containing the hosts you want this job to manage.</p>",
|
awPopOver: i18n._("<p>Select the inventory containing the hosts you want this job to manage.</p>"),
|
||||||
dataTitle: 'Inventory',
|
dataTitle: i18n._('Inventory'),
|
||||||
dataPlacement: 'right',
|
dataPlacement: 'right',
|
||||||
dataContainer: "body",
|
dataContainer: "body",
|
||||||
ngShow: "selectedTemplate.ask_inventory_on_launch",
|
ngShow: "selectedTemplate.ask_inventory_on_launch",
|
||||||
@@ -90,16 +96,16 @@ export default
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
job_type: {
|
job_type: {
|
||||||
label: 'Job Type',
|
label: i18n._('Job Type'),
|
||||||
type: 'select',
|
type: 'select',
|
||||||
ngOptions: 'type.label for type in job_type_options track by type.value',
|
ngOptions: 'type.label for type in job_type_options track by type.value',
|
||||||
"default": 0,
|
"default": 0,
|
||||||
class: 'Form-formGroup--fullWidth',
|
class: 'Form-formGroup--fullWidth',
|
||||||
awPopOver: "<p>When this template is submitted as a job, setting the type to <em>run</em> will execute the playbook, running tasks " +
|
awPopOver: i18n._("<p>When this template is submitted as a job, setting the type to <em>run</em> will execute the playbook, running tasks " +
|
||||||
" on the selected hosts.</p> <p>Setting the type to <em>check</em> will not execute the playbook. Instead, <code>ansible</code> will check playbook " +
|
" on the selected hosts.</p> <p>Setting the type to <em>check</em> will not execute the playbook. Instead, <code>ansible</code> will check playbook " +
|
||||||
" syntax, test environment setup and report problems.</p> <p>Setting the type to <em>scan</em> will execute the playbook and store any " +
|
" syntax, test environment setup and report problems.</p> <p>Setting the type to <em>scan</em> will execute the playbook and store any " +
|
||||||
" scanned facts for use with Tower's System Tracking feature.</p>",
|
" scanned facts for use with Tower's System Tracking feature.</p>"),
|
||||||
dataTitle: 'Job Type',
|
dataTitle: i18n._('Job Type'),
|
||||||
dataPlacement: 'right',
|
dataPlacement: 'right',
|
||||||
dataContainer: "body",
|
dataContainer: "body",
|
||||||
ngShow: "selectedTemplate.ask_job_type_on_launch",
|
ngShow: "selectedTemplate.ask_job_type_on_launch",
|
||||||
@@ -109,43 +115,43 @@ export default
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
limit: {
|
limit: {
|
||||||
label: 'Limit',
|
label: i18n._('Limit'),
|
||||||
type: 'text',
|
type: 'text',
|
||||||
class: 'Form-formGroup--fullWidth',
|
class: 'Form-formGroup--fullWidth',
|
||||||
awPopOver: "<p>Provide a host pattern to further constrain the list of hosts that will be managed or affected by the playbook. " +
|
awPopOver: i18n._("<p>Provide a host pattern to further constrain the list of hosts that will be managed or affected by the playbook. " +
|
||||||
"Multiple patterns can be separated by ; : or ,</p><p>For more information and examples see " +
|
"Multiple patterns can be separated by ; : or ,</p><p>For more information and examples see " +
|
||||||
"<a href=\"http://docs.ansible.com/intro_patterns.html\" target=\"_blank\">the Patterns topic at docs.ansible.com</a>.</p>",
|
"<a href=\"http://docs.ansible.com/intro_patterns.html\" target=\"_blank\">the Patterns topic at docs.ansible.com</a>.</p>"),
|
||||||
dataTitle: 'Limit',
|
dataTitle: i18n._('Limit'),
|
||||||
dataPlacement: 'right',
|
dataPlacement: 'right',
|
||||||
dataContainer: "body",
|
dataContainer: "body",
|
||||||
ngShow: "selectedTemplate.ask_limit_on_launch",
|
ngShow: "selectedTemplate.ask_limit_on_launch",
|
||||||
ngDisabled: '!canAddWorkflowJobTemplate'
|
ngDisabled: '!canAddWorkflowJobTemplate'
|
||||||
},
|
},
|
||||||
job_tags: {
|
job_tags: {
|
||||||
label: 'Job Tags',
|
label: i18n._('Job Tags'),
|
||||||
type: 'textarea',
|
type: 'textarea',
|
||||||
rows: 5,
|
rows: 5,
|
||||||
'elementClass': 'Form-textInput',
|
'elementClass': 'Form-textInput',
|
||||||
class: 'Form-formGroup--fullWidth',
|
class: 'Form-formGroup--fullWidth',
|
||||||
awPopOver: "<p>Provide a comma separated list of tags.</p>\n" +
|
awPopOver: i18n._("<p>Provide a comma separated list of tags.</p>\n" +
|
||||||
"<p>Tags are useful when you have a large playbook, and you want to run a specific part of a play or task.</p>" +
|
"<p>Tags are useful when you have a large playbook, and you want to run a specific part of a play or task.</p>" +
|
||||||
"<p>Consult the Ansible documentation for further details on the usage of tags.</p>",
|
"<p>Consult the Ansible documentation for further details on the usage of tags.</p>"),
|
||||||
dataTitle: "Job Tags",
|
dataTitle: i18n._("Job Tags"),
|
||||||
dataPlacement: "right",
|
dataPlacement: "right",
|
||||||
dataContainer: "body",
|
dataContainer: "body",
|
||||||
ngShow: "selectedTemplate.ask_tags_on_launch",
|
ngShow: "selectedTemplate.ask_tags_on_launch",
|
||||||
ngDisabled: '!canAddWorkflowJobTemplate'
|
ngDisabled: '!canAddWorkflowJobTemplate'
|
||||||
},
|
},
|
||||||
skip_tags: {
|
skip_tags: {
|
||||||
label: 'Skip Tags',
|
label: i18n._('Skip Tags'),
|
||||||
type: 'textarea',
|
type: 'textarea',
|
||||||
rows: 5,
|
rows: 5,
|
||||||
'elementClass': 'Form-textInput',
|
'elementClass': 'Form-textInput',
|
||||||
class: 'Form-formGroup--fullWidth',
|
class: 'Form-formGroup--fullWidth',
|
||||||
awPopOver: "<p>Provide a comma separated list of tags.</p>\n" +
|
awPopOver: i18n._("<p>Provide a comma separated list of tags.</p>\n" +
|
||||||
"<p>Skip tags are useful when you have a large playbook, and you want to skip specific parts of a play or task.</p>" +
|
"<p>Skip tags are useful when you have a large playbook, and you want to skip specific parts of a play or task.</p>" +
|
||||||
"<p>Consult the Ansible documentation for further details on the usage of tags.</p>",
|
"<p>Consult the Ansible documentation for further details on the usage of tags.</p>"),
|
||||||
dataTitle: "Skip Tags",
|
dataTitle: i18n._("Skip Tags"),
|
||||||
dataPlacement: "right",
|
dataPlacement: "right",
|
||||||
dataContainer: "body",
|
dataContainer: "body",
|
||||||
ngShow: "selectedTemplate.ask_skip_tags_on_launch",
|
ngShow: "selectedTemplate.ask_skip_tags_on_launch",
|
||||||
@@ -167,7 +173,7 @@ export default
|
|||||||
ngShow: 'canAddWorkflowJobTemplate'
|
ngShow: 'canAddWorkflowJobTemplate'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
};}])
|
||||||
.factory('WorkflowMakerForm', ['WorkflowMakerFormObject', 'NotificationsList', function(WorkflowMakerFormObject, NotificationsList) {
|
.factory('WorkflowMakerForm', ['WorkflowMakerFormObject', 'NotificationsList', function(WorkflowMakerFormObject, NotificationsList) {
|
||||||
return function() {
|
return function() {
|
||||||
var itm;
|
var itm;
|
||||||
|
|||||||
@@ -13,9 +13,10 @@
|
|||||||
export default
|
export default
|
||||||
angular.module('WorkflowFormDefinition', [])
|
angular.module('WorkflowFormDefinition', [])
|
||||||
|
|
||||||
.value ('WorkflowFormObject', {
|
.factory('WorkflowFormObject', ['i18n', function(i18n) {
|
||||||
|
return {
|
||||||
|
|
||||||
addTitle: 'New Workflow',
|
addTitle: i18n._('New Workflow'),
|
||||||
editTitle: '{{ name }}',
|
editTitle: '{{ name }}',
|
||||||
name: 'workflow_job_template',
|
name: 'workflow_job_template',
|
||||||
base: 'workflow',
|
base: 'workflow',
|
||||||
@@ -27,57 +28,57 @@ export default
|
|||||||
|
|
||||||
fields: {
|
fields: {
|
||||||
name: {
|
name: {
|
||||||
label: 'Name',
|
label: i18n._('Name'),
|
||||||
type: 'text',
|
type: 'text',
|
||||||
required: true,
|
required: true,
|
||||||
ngDisabled: '!(workflow_job_template_obj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)',
|
ngDisabled: '!(workflow_job_template_obj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)',
|
||||||
column: 1
|
column: 1
|
||||||
},
|
},
|
||||||
description: {
|
description: {
|
||||||
label: 'Description',
|
label: i18n._('Description'),
|
||||||
type: 'text',
|
type: 'text',
|
||||||
column: 1,
|
column: 1,
|
||||||
ngDisabled: '!(workflow_job_template_obj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)'
|
ngDisabled: '!(workflow_job_template_obj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)'
|
||||||
},
|
},
|
||||||
organization: {
|
organization: {
|
||||||
label: 'Organization',
|
label: i18n._('Organization'),
|
||||||
type: 'lookup',
|
type: 'lookup',
|
||||||
sourceModel: 'organization',
|
sourceModel: 'organization',
|
||||||
basePath: 'organizations',
|
basePath: 'organizations',
|
||||||
list: 'OrganizationList',
|
list: 'OrganizationList',
|
||||||
sourceField: 'name',
|
sourceField: 'name',
|
||||||
dataTitle: 'Organization',
|
dataTitle: i18n._('Organization'),
|
||||||
dataContainer: 'body',
|
dataContainer: 'body',
|
||||||
dataPlacement: 'right',
|
dataPlacement: 'right',
|
||||||
column: 1,
|
column: 1,
|
||||||
ngDisabled: '!(workflow_job_template_obj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)'
|
ngDisabled: '!(workflow_job_template_obj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)'
|
||||||
},
|
},
|
||||||
labels: {
|
labels: {
|
||||||
label: 'Labels',
|
label: i18n._('Labels'),
|
||||||
type: 'select',
|
type: 'select',
|
||||||
class: 'Form-formGroup--fullWidth',
|
class: 'Form-formGroup--fullWidth',
|
||||||
ngOptions: 'label.label for label in labelOptions track by label.value',
|
ngOptions: 'label.label for label in labelOptions track by label.value',
|
||||||
multiSelect: true,
|
multiSelect: true,
|
||||||
dataTitle: 'Labels',
|
dataTitle: i18n._('Labels'),
|
||||||
dataPlacement: 'right',
|
dataPlacement: 'right',
|
||||||
awPopOver: "<p>Optional labels that describe this job template, such as 'dev' or 'test'. Labels can be used to group and filter job templates and completed jobs in the Tower display.</p>",
|
awPopOver: i18n._("<p>Optional labels that describe this job template, such as 'dev' or 'test'. Labels can be used to group and filter job templates and completed jobs in the Tower display.</p>"),
|
||||||
dataContainer: 'body',
|
dataContainer: 'body',
|
||||||
ngDisabled: '!(workflow_job_template_obj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)'
|
ngDisabled: '!(workflow_job_template_obj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)'
|
||||||
},
|
},
|
||||||
variables: {
|
variables: {
|
||||||
label: 'Extra Variables',
|
label: i18n._('Extra Variables'),
|
||||||
type: 'textarea',
|
type: 'textarea',
|
||||||
class: 'Form-textAreaLabel Form-formGroup--fullWidth',
|
class: 'Form-textAreaLabel Form-formGroup--fullWidth',
|
||||||
rows: 6,
|
rows: 6,
|
||||||
"default": "---",
|
"default": "---",
|
||||||
column: 2,
|
column: 2,
|
||||||
awPopOver: "<p>Pass extra command line variables to the playbook. This is the <code>-e</code> or <code>--extra-vars</code> command line parameter " +
|
awPopOver: i18n._("<p>Pass extra command line variables to the playbook. This is the <code>-e</code> or <code>--extra-vars</code> command line parameter " +
|
||||||
"for <code>ansible-playbook</code>. Provide key/value pairs using either YAML or JSON.</p>" +
|
"for <code>ansible-playbook</code>. Provide key/value pairs using either YAML or JSON.</p>" +
|
||||||
"JSON:<br />\n" +
|
"JSON:<br />\n" +
|
||||||
"<blockquote>{<br /> \"somevar\": \"somevalue\",<br /> \"password\": \"magic\"<br /> }</blockquote>\n" +
|
"<blockquote>{<br /> \"somevar\": \"somevalue\",<br /> \"password\": \"magic\"<br /> }</blockquote>\n" +
|
||||||
"YAML:<br />\n" +
|
"YAML:<br />\n" +
|
||||||
"<blockquote>---<br />somevar: somevalue<br />password: magic<br /></blockquote>\n",
|
"<blockquote>---<br />somevar: somevalue<br />password: magic<br /></blockquote>\n"),
|
||||||
dataTitle: 'Extra Variables',
|
dataTitle: i18n._('Extra Variables'),
|
||||||
dataPlacement: 'right',
|
dataPlacement: 'right',
|
||||||
dataContainer: "body",
|
dataContainer: "body",
|
||||||
ngDisabled: '!(workflow_job_template_obj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)' // TODO: get working
|
ngDisabled: '!(workflow_job_template_obj.summary_fields.user_capabilities.edit || canAddWorkflowJobTemplate)' // TODO: get working
|
||||||
@@ -102,18 +103,20 @@ export default
|
|||||||
|
|
||||||
related: {
|
related: {
|
||||||
permissions: {
|
permissions: {
|
||||||
awToolTip: 'Please save before assigning permissions',
|
awToolTip: i18n._('Please save before assigning permissions'),
|
||||||
dataPlacement: 'top',
|
dataPlacement: 'top',
|
||||||
basePath: 'job_templates/:id/access_list/',
|
basePath: 'api/v1/workflow_job_templates/{{$stateParams.workflow_job_template_id}}/access_list/',
|
||||||
|
search: {
|
||||||
|
order_by: 'username'
|
||||||
|
},
|
||||||
type: 'collection',
|
type: 'collection',
|
||||||
title: 'Permissions',
|
title: i18n._('Permissions'),
|
||||||
iterator: 'permission',
|
iterator: 'permission',
|
||||||
index: false,
|
index: false,
|
||||||
open: false,
|
open: false,
|
||||||
searchType: 'select',
|
|
||||||
actions: {
|
actions: {
|
||||||
add: {
|
add: {
|
||||||
ngClick: "addPermission",
|
ngClick: "$state.go('.add')",
|
||||||
label: 'Add',
|
label: 'Add',
|
||||||
awToolTip: 'Add a permission',
|
awToolTip: 'Add a permission',
|
||||||
actionClass: 'btn List-buttonSubmit',
|
actionClass: 'btn List-buttonSubmit',
|
||||||
@@ -134,14 +137,12 @@ export default
|
|||||||
type: 'role',
|
type: 'role',
|
||||||
noSort: true,
|
noSort: true,
|
||||||
class: 'col-lg-4 col-md-4 col-sm-4 col-xs-4',
|
class: 'col-lg-4 col-md-4 col-sm-4 col-xs-4',
|
||||||
searchable: false
|
|
||||||
},
|
},
|
||||||
team_roles: {
|
team_roles: {
|
||||||
label: 'Team Roles',
|
label: 'Team Roles',
|
||||||
type: 'team_roles',
|
type: 'team_roles',
|
||||||
noSort: true,
|
noSort: true,
|
||||||
class: 'col-lg-5 col-md-5 col-sm-5 col-xs-4',
|
class: 'col-lg-5 col-md-5 col-sm-5 col-xs-4',
|
||||||
searchable: false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -155,23 +156,23 @@ export default
|
|||||||
ngClick: 'addSurvey()',
|
ngClick: 'addSurvey()',
|
||||||
ngShow: '!survey_exists',
|
ngShow: '!survey_exists',
|
||||||
awFeature: 'surveys',
|
awFeature: 'surveys',
|
||||||
awToolTip: 'Please save before adding a survey',
|
awToolTip: i18n._('Please save before adding a survey'),
|
||||||
dataPlacement: 'top',
|
dataPlacement: 'top',
|
||||||
label: 'Add Survey',
|
label: i18n._('Add Survey'),
|
||||||
class: 'Form-primaryButton'
|
class: 'Form-primaryButton'
|
||||||
},
|
},
|
||||||
edit_survey: {
|
edit_survey: {
|
||||||
ngClick: 'editSurvey()',
|
ngClick: 'editSurvey()',
|
||||||
awFeature: 'surveys',
|
awFeature: 'surveys',
|
||||||
ngShow: 'survey_exists',
|
ngShow: 'survey_exists',
|
||||||
label: 'Edit Survey',
|
label: i18n._('Edit Survey'),
|
||||||
class: 'Form-primaryButton'
|
class: 'Form-primaryButton'
|
||||||
},
|
},
|
||||||
workflow_editor: {
|
workflow_editor: {
|
||||||
ngClick: 'openWorkflowMaker()',
|
ngClick: 'openWorkflowMaker()',
|
||||||
awToolTip: 'Please save before defining the workflow graph',
|
awToolTip: i18n._('Please save before defining the workflow graph'),
|
||||||
dataPlacement: 'top',
|
dataPlacement: 'top',
|
||||||
label: 'Workflow Editor',
|
label: i18n._('Workflow Editor'),
|
||||||
class: 'Form-primaryButton'
|
class: 'Form-primaryButton'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -188,7 +189,7 @@ export default
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
})
|
};}])
|
||||||
|
|
||||||
.factory('WorkflowForm', ['WorkflowFormObject', 'NotificationsList',
|
.factory('WorkflowForm', ['WorkflowFormObject', 'NotificationsList',
|
||||||
function(WorkflowFormObject, NotificationsList) {
|
function(WorkflowFormObject, NotificationsList) {
|
||||||
|
|||||||
Reference in New Issue
Block a user