1
0
mirror of https://github.com/ansible/awx.git synced 2024-10-30 22:21:13 +03:00

Merge pull request #3781 from jaredevantabor/i18n

i18n on the UI
This commit is contained in:
Jared Tabor 2016-10-25 15:59:27 -07:00 committed by GitHub
commit bf301ec130
60 changed files with 3120 additions and 806 deletions

View File

@ -81,7 +81,7 @@ SETUP_TAR_CHECKSUM=$(NAME)-setup-CHECKSUM
# DEB build parameters
DEBUILD_BIN ?= debuild
DEBUILD_OPTS =
DEBUILD_OPTS =
DPUT_BIN ?= dput
DPUT_OPTS ?= -c .dput.cf -u
REPREPRO_BIN ?= reprepro
@ -506,6 +506,43 @@ test_jenkins : test_coverage
# UI TASKS
# --------------------------------------
HAVE_PO := $(shell ls awx/ui/po/*.po 2>/dev/null)
check-po:
ifdef HAVE_PO
# Should be 'Language: zh-CN' but not 'Language: zh_CN' in zh_CN.po
for po in awx/ui/po/*.po ; do \
echo $$po; \
mo="awx/ui/po/`basename $$po .po`.mo"; \
msgfmt --check --verbose $$po -o $$mo; \
if test "$$?" -ne 0 ; then \
exit -1; \
fi; \
rm $$mo; \
name=`echo "$$po" | grep '-'`; \
if test "x$$name" != x ; then \
right_name=`echo $$language | sed -e 's/-/_/'`; \
echo "ERROR: WRONG $$name CORRECTION: $$right_name"; \
exit -1; \
fi; \
language=`grep '^"Language:' "$$po" | grep '_'`; \
if test "x$$language" != x ; then \
right_language=`echo $$language | sed -e 's/_/-/'`; \
echo "ERROR: WRONG $$language CORRECTION: $$right_language in $$po"; \
exit -1; \
fi; \
done;
else
@echo No PO files
endif
# generate l10n .json
languages: $(UI_DEPS_FLAG_FILE) check-po
$(NPM_BIN) --prefix awx/ui run languages
# generate .pot
pot: $(UI_DEPS_FLAG_FILE)
$(NPM_BIN) --prefix awx/ui run pot
ui-deps: $(UI_DEPS_FLAG_FILE)
$(UI_DEPS_FLAG_FILE): awx/ui/package.json
@ -520,6 +557,7 @@ ui-docker: $(UI_DEPS_FLAG_FILE)
ui-release: $(UI_RELEASE_FLAG_FILE)
# todo: include languages target when .po deliverables are added to source control
$(UI_RELEASE_FLAG_FILE): $(UI_DEPS_FLAG_FILE)
$(NPM_BIN) --prefix awx/ui run build-release
touch $(UI_RELEASE_FLAG_FILE)

View File

@ -16,6 +16,7 @@ module.exports = function(grunt) {
// Project configuration.
grunt.initConfig(configs);
grunt.loadNpmTasks('grunt-newer');
grunt.loadNpmTasks('grunt-angular-gettext');
// writes environment variables for development. current manages:
// browser-sync + websocket proxy

View File

@ -1,5 +1,6 @@
export default
['$scope', '$state', 'ConfigService', function($scope, $state, ConfigService){
['$scope', '$state', 'ConfigService', 'i18n',
function($scope, $state, ConfigService, i18n){
var processVersion = function(version){
// prettify version & calculate padding
// e,g 3.0.0-0.git201602191743/ -> 3.0.0
@ -20,6 +21,7 @@ export default
.then(function(config){
$scope.subscription = config.license_info.subscription_name;
$scope.version = processVersion(config.version);
$scope.version_str = i18n._("Version");
$('#about-modal').modal('show');
});
};

View File

@ -11,7 +11,7 @@
<!-- Don't indent this properly, you'll break the cow -->
<pre class="About-cowsay--code">
________________
/ Tower Version \\
/ Tower {{version_str}} \\
\\<span>{{version}}</span>/
----------------
\\ ^__^

View File

@ -7,6 +7,7 @@
// Vendor dependencies
import 'jquery';
import 'angular';
import 'angular-gettext';
import 'bootstrap';
import 'jquery-ui';
import 'bootstrap-datepicker';
@ -79,6 +80,7 @@ import config from './shared/config/main';
import './login/authenticationServices/pendo/ng-pendo';
import footer from './footer/main';
import scheduler from './scheduler/main';
import {N_} from './i18n';
var tower = angular.module('Tower', [
// how to add CommonJS / AMD third-party dependencies:
@ -203,6 +205,8 @@ var tower = angular.module('Tower', [
scheduler.name,
'ApiModelHelper',
'ActivityStreamHelper',
'gettext',
'I18N',
])
.constant('AngularScheduler.partials', urlPrefix + 'lib/angular-scheduler/lib/')
@ -237,6 +241,10 @@ var tower = angular.module('Tower', [
$state.go('dashboard');
});
/* Mark translatable strings with N_() and
* extract them by 'grunt nggettext_extract'
* but angular.config() cannot get gettextCatalog.
*/
$stateProvider.
state('teams', {
url: '/teams',
@ -248,7 +256,7 @@ var tower = angular.module('Tower', [
},
ncyBreadcrumb: {
parent: 'setup',
label: 'TEAMS'
label: N_("TEAMS")
}
}).
@ -258,7 +266,7 @@ var tower = angular.module('Tower', [
controller: TeamsAdd,
ncyBreadcrumb: {
parent: "teams",
label: "CREATE TEAM"
label: N_("CREATE TEAM")
}
}).
@ -333,7 +341,7 @@ var tower = angular.module('Tower', [
},
ncyBreadcrumb: {
parent: 'setup',
label: 'CREDENTIALS'
label: N_("CREDENTIALS")
}
}).
@ -343,7 +351,7 @@ var tower = angular.module('Tower', [
controller: CredentialsAdd,
ncyBreadcrumb: {
parent: "credentials",
label: "CREATE CREDENTIAL"
label: N_("CREATE CREDENTIAL")
}
}).
@ -370,7 +378,7 @@ var tower = angular.module('Tower', [
},
ncyBreadcrumb: {
parent: 'setup',
label: 'USERS'
label: N_("USERS")
}
}).
@ -380,7 +388,7 @@ var tower = angular.module('Tower', [
controller: UsersAdd,
ncyBreadcrumb: {
parent: "users",
label: "CREATE USER"
label: N_("CREATE USER")
}
}).
@ -420,7 +428,7 @@ var tower = angular.module('Tower', [
templateUrl: urlPrefix + 'partials/sockets.html',
controller: SocketsController,
ncyBreadcrumb: {
label: 'SOCKETS'
label: N_("SOCKETS")
}
});
}
@ -443,13 +451,14 @@ var tower = angular.module('Tower', [
'CheckLicense', '$location', 'Authorization', 'LoadBasePaths', 'Timer',
'ClearScope', 'LoadConfig', 'Store', 'pendoService', 'Prompt', 'Rest',
'Wait', 'ProcessErrors', '$state', 'GetBasePath', 'ConfigService',
'FeaturesService', '$filter', 'SocketService',
'FeaturesService', '$filter', 'SocketService', 'I18NInit',
function($stateExtender, $q, $compile, $cookieStore, $rootScope, $log,
CheckLicense, $location, Authorization, LoadBasePaths, Timer,
ClearScope, LoadConfig, Store, pendoService, Prompt, Rest, Wait,
ProcessErrors, $state, GetBasePath, ConfigService, FeaturesService,
$filter, SocketService) {
$filter, SocketService, I18NInit) {
I18NInit();
$stateExtender.addState({
name: 'dashboard',
url: '/home',
@ -466,7 +475,7 @@ var tower = angular.module('Tower', [
refreshButton: true
},
ncyBreadcrumb: {
label: "DASHBOARD"
label: N_("DASHBOARD")
},
resolve: {
graphData: ['$q', 'jobStatusGraphData', '$rootScope',
@ -487,7 +496,7 @@ var tower = angular.module('Tower', [
templateUrl: urlPrefix + 'partials/jobs.html',
controller: JobsListController,
ncyBreadcrumb: {
label: "JOBS"
label: N_("JOBS")
},
params: {
search: {
@ -512,7 +521,7 @@ var tower = angular.module('Tower', [
activityStreamTarget: 'project'
},
ncyBreadcrumb: {
label: "PROJECTS"
label: N_("PROJECTS")
},
socket: {
"groups":{
@ -528,7 +537,7 @@ var tower = angular.module('Tower', [
controller: ProjectsAdd,
ncyBreadcrumb: {
parent: "projects",
label: "CREATE PROJECT"
label: N_("CREATE PROJECT")
},
socket: {
"groups":{

View File

@ -3,7 +3,7 @@
<div class="BreadCrumb-menuLinkHolder">
<div class="BreadCrumb-menuLink"
id="bread_crumb_refresh"
aw-tool-tip="Refresh the page"
aw-tool-tip="{{'Refresh the page'|translate}}"
data-placement="left"
data-trigger="hover"
data-container="body"

View File

@ -15,7 +15,8 @@ export function ProjectsList ($scope, $rootScope, $location, $log, $stateParams,
Rest, Alert, ProjectList, GenerateList, Prompt, SearchInit,
PaginateInit, ReturnToCaller, ClearScope, ProcessErrors, GetBasePath,
SelectionInit, ProjectUpdate, Refresh, Wait, GetChoices, Empty,
Find, GetProjectIcon, GetProjectToolTip, $filter, $state, rbacUiControlService) {
Find, GetProjectIcon, GetProjectToolTip, $filter, $state, rbacUiControlService,
i18n) {
ClearScope();
$scope.canAdd = false;
@ -57,16 +58,16 @@ export function ProjectsList ($scope, $rootScope, $location, $log, $stateParams,
$scope.projects.forEach(function(project, i) {
$scope.projects[i].statusIcon = GetProjectIcon(project.status);
$scope.projects[i].statusTip = GetProjectToolTip(project.status);
$scope.projects[i].scm_update_tooltip = "Start an SCM update";
$scope.projects[i].scm_schedule_tooltip = "Schedule future SCM updates";
$scope.projects[i].scm_update_tooltip = i18n._("Start an SCM update");
$scope.projects[i].scm_schedule_tooltip = i18n._("Schedule future SCM updates");
$scope.projects[i].scm_type_class = "";
if (project.status === 'failed' && project.summary_fields.last_update && project.summary_fields.last_update.status === 'canceled') {
$scope.projects[i].statusTip = 'Canceled. Click for details';
$scope.projects[i].statusTip = i18n._('Canceled. Click for details');
}
if (project.status === 'running' || project.status === 'updating') {
$scope.projects[i].scm_update_tooltip = "SCM update currently running";
$scope.projects[i].scm_update_tooltip = i18n._("SCM update currently running");
$scope.projects[i].scm_type_class = "btn-disabled";
}
@ -74,10 +75,10 @@ export function ProjectsList ($scope, $rootScope, $location, $log, $stateParams,
if (type.value === project.scm_type) {
$scope.projects[i].scm_type = type.label;
if (type.label === 'Manual') {
$scope.projects[i].scm_update_tooltip = 'Manual projects do not require an SCM update';
$scope.projects[i].scm_schedule_tooltip = 'Manual projects do not require a schedule';
$scope.projects[i].scm_update_tooltip = i18n._('Manual projects do not require an SCM update');
$scope.projects[i].scm_schedule_tooltip = i18n._('Manual projects do not require a schedule');
$scope.projects[i].scm_type_class = 'btn-disabled';
$scope.projects[i].statusTip = 'Not configured for SCM';
$scope.projects[i].statusTip = i18n._('Not configured for SCM');
$scope.projects[i].statusIcon = 'none';
}
}
@ -215,8 +216,8 @@ export function ProjectsList ($scope, $rootScope, $location, $log, $stateParams,
$state.go('scmUpdateStdout', {id: id});
} else {
Alert('No Updates Available', 'There is no SCM update information available for this project. An update has not yet been ' +
' completed. If you have not already done so, start an update for this project.', 'alert-info');
Alert(i18n._('No Updates Available'), i18n._('There is no SCM update information available for this project. An update has not yet been ' +
' completed. If you have not already done so, start an update for this project.'), 'alert-info');
}
});
@ -224,8 +225,8 @@ export function ProjectsList ($scope, $rootScope, $location, $log, $stateParams,
// Refresh the project list
var project = Find({ list: $scope.projects, key: 'id', val: id });
if (Empty(project.scm_type) || project.scm_type === 'Manual') {
Alert('No SCM Configuration', 'The selected project is not configured for SCM. To configure for SCM, edit the project and provide SCM settings, ' +
'and then run an update.', 'alert-info');
Alert(i18n._('No SCM Configuration'), i18n._('The selected project is not configured for SCM. To configure for SCM, edit the project and provide SCM settings, ' +
'and then run an update.'), 'alert-info');
} else {
// Refresh what we have in memory to insure we're accessing the most recent status record
Rest.setUrl(project.url);
@ -234,8 +235,8 @@ export function ProjectsList ($scope, $rootScope, $location, $log, $stateParams,
$scope.$emit('GoToJobDetails', data);
})
.error(function(data, status) {
ProcessErrors($scope, data, status, null, { hdr: 'Error!',
msg: 'Project lookup failed. GET returned: ' + status });
ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'),
msg: i18n._('Project lookup failed. GET returned: ') + status });
});
}
};
@ -255,14 +256,14 @@ export function ProjectsList ($scope, $rootScope, $location, $log, $stateParams,
}
})
.error(function (data, status) {
ProcessErrors($scope, data, status, null, { hdr: 'Error!',
msg: 'Call to ' + url + ' failed. DELETE returned status: ' + status });
ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'),
msg: i18n.format(i18n._('Call to %s failed. DELETE returned status: '), url) + status });
});
};
Prompt({
hdr: 'Delete',
body: '<div class="Prompt-bodyQuery">Are you sure you want to delete the project below?</div><div class="Prompt-bodyTarget">' + $filter('sanitize')(name) + '</div>',
hdr: i18n._('Delete'),
body: i18n._('<div class="Prompt-bodyQuery">Are you sure you want to delete the project below?</div>') + '<div class="Prompt-bodyTarget">' + $filter('sanitize')(name) + '</div>',
action: action,
actionText: 'DELETE'
});
@ -276,11 +277,11 @@ export function ProjectsList ($scope, $rootScope, $location, $log, $stateParams,
Rest.setUrl(url);
Rest.post()
.success(function () {
Alert('SCM Update Cancel', 'Your request to cancel the update was submitted to the task manager.', 'alert-info');
Alert(i18n._('SCM Update Cancel'), i18n._('Your request to cancel the update was submitted to the task manager.'), 'alert-info');
$scope.refresh();
})
.error(function (data, status) {
ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Call to ' + url + ' failed. POST status: ' + status });
ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'), msg: i18n.format(i18n._('Call to %s failed. POST status: '), url) + status });
});
});
@ -296,12 +297,12 @@ export function ProjectsList ($scope, $rootScope, $location, $log, $stateParams,
if (data.can_cancel) {
$scope.$emit('Cancel_Update', url);
} else {
Alert('Cancel Not Allowed', '<div>Either you do not have access or the SCM update process completed. ' +
'Click the <em>Refresh</em> button to view the latest status.</div>', 'alert-info', null, null, null, null, true);
Alert(i18n._('Cancel Not Allowed'), i18n._('<div>Either you do not have access or the SCM update process completed. ' +
'Click the <em>Refresh</em> button to view the latest status.</div>'), 'alert-info', null, null, null, null, true);
}
})
.error(function (data, status) {
ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Call to ' + url + ' failed. GET status: ' + status });
ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'), msg: i18n.format(i18n._('Call to %s failed. GET status: '), url) + status });
});
});
@ -316,17 +317,17 @@ export function ProjectsList ($scope, $rootScope, $location, $log, $stateParams,
$scope.$emit('Check_Cancel', data);
})
.error(function (data, status) {
ProcessErrors($scope, data, status, null, { hdr: 'Error!',
msg: 'Call to ' + data.related.current_update + ' failed. GET status: ' + status });
ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'),
msg: i18n.format(i18n._('Call to %s failed. GET status: '), data.related.current_update) + status });
});
} else {
Alert('Update Not Found', '<div>An SCM update does not appear to be running for project: ' + $filter('sanitize')(name) + '. Click the <em>Refresh</em> ' +
'button to view the latest status.</div>', 'alert-info',undefined,undefined,undefined,undefined,true);
Alert(i18n._('Update Not Found'), i18n.format(i18n._('<div>An SCM update does not appear to be running for project: %s. Click the <em>Refresh</em> ' +
'button to view the latest status.</div>'), $filter('sanitize')(name)), 'alert-info',undefined,undefined,undefined,undefined,true);
}
})
.error(function (data, status) {
ProcessErrors($scope, data, status, null, { hdr: 'Error!',
msg: 'Call to get project failed. GET status: ' + status });
ProcessErrors($scope, data, status, null, { hdr: i18n._('Error!'),
msg: i18n._('Call to get project failed. GET status: ') + status });
});
};
@ -371,7 +372,8 @@ ProjectsList.$inject = ['$scope', '$rootScope', '$location', '$log',
'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope',
'ProcessErrors', 'GetBasePath', 'SelectionInit', 'ProjectUpdate',
'Refresh', 'Wait', 'GetChoices', 'Empty', 'Find',
'GetProjectIcon', 'GetProjectToolTip', '$filter', '$state', 'rbacUiControlService'
'GetProjectIcon', 'GetProjectToolTip', '$filter', '$state', 'rbacUiControlService',
'i18n'
];
@ -379,7 +381,7 @@ export function ProjectsAdd(Refresh, $scope, $rootScope, $compile, $location, $l
$stateParams, ProjectsForm, GenerateForm, Rest, Alert, ProcessErrors,
ClearScope, GetBasePath, ReturnToCaller, GetProjectPath, LookUpInit,
OrganizationList, CredentialList, GetChoices, DebugForm, Wait, $state,
CreateSelect2) {
CreateSelect2, i18n) {
Rest.setUrl(GetBasePath('projects'));
Rest.options()
@ -504,8 +506,8 @@ export function ProjectsAdd(Refresh, $scope, $rootScope, $compile, $location, $l
})
.error(function (data, status) {
Wait('stop');
ProcessErrors($scope, data, status, form, { hdr: 'Error!',
msg: 'Failed to create new project. POST returned status: ' + status });
ProcessErrors($scope, data, status, form, { hdr: i18n._('Error!'),
msg: i18n._('Failed to create new project. POST returned status: ') + status });
});
};
@ -521,27 +523,27 @@ export function ProjectsAdd(Refresh, $scope, $rootScope, $compile, $location, $l
if($scope.scm_type.value) {
switch ($scope.scm_type.value) {
case 'git':
$scope.urlPopover = '<p>Example URLs for GIT SCM include:</p><ul class=\"no-bullets\"><li>https://github.com/ansible/ansible.git</li>' +
$scope.urlPopover = i18n._('<p>Example URLs for GIT SCM include:</p><ul class=\"no-bullets\"><li>https://github.com/ansible/ansible.git</li>' +
'<li>git@github.com:ansible/ansible.git</li><li>git://servername.example.com/ansible.git</li></ul>' +
'<p><strong>Note:</strong> When using SSH protocol for GitHub or Bitbucket, enter an SSH key only, ' +
'do not enter a username (other than git). Additionally, GitHub and Bitbucket do not support password authentication when using ' +
'SSH. GIT read only protocol (git://) does not use username or password information.';
'SSH. GIT read only protocol (git://) does not use username or password information.');
break;
case 'svn':
$scope.urlPopover = '<p>Example URLs for Subversion SCM include:</p>' +
$scope.urlPopover = i18n._('<p>Example URLs for Subversion SCM include:</p>' +
'<ul class=\"no-bullets\"><li>https://github.com/ansible/ansible</li><li>svn://servername.example.com/path</li>' +
'<li>svn+ssh://servername.example.com/path</li></ul>';
'<li>svn+ssh://servername.example.com/path</li></ul>');
break;
case 'hg':
$scope.urlPopover = '<p>Example URLs for Mercurial SCM include:</p>' +
$scope.urlPopover = i18n._('<p>Example URLs for Mercurial SCM include:</p>' +
'<ul class=\"no-bullets\"><li>https://bitbucket.org/username/project</li><li>ssh://hg@bitbucket.org/username/project</li>' +
'<li>ssh://server.example.com/path</li></ul>' +
'<p><strong>Note:</strong> Mercurial does not support password authentication for SSH. ' +
'Do not put the username and key in the URL. ' +
'If using Bitbucket and SSH, do not supply your Bitbucket username.';
'If using Bitbucket and SSH, do not supply your Bitbucket username.');
break;
default:
$scope.urlPopover = '<p> URL popover text';
$scope.urlPopover = i18n._('<p> URL popover text');
}
}
@ -556,7 +558,7 @@ ProjectsAdd.$inject = ['Refresh', '$scope', '$rootScope', '$compile', '$location
'$stateParams', 'ProjectsForm', 'GenerateForm', 'Rest', 'Alert',
'ProcessErrors', 'ClearScope', 'GetBasePath', 'ReturnToCaller',
'GetProjectPath', 'LookUpInit', 'OrganizationList', 'CredentialList',
'GetChoices', 'DebugForm', 'Wait', '$state', 'CreateSelect2'
'GetChoices', 'DebugForm', 'Wait', '$state', 'CreateSelect2', 'i18n'
];
@ -566,7 +568,7 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
ReturnToCaller, GetProjectPath, Authorization, CredentialList, LookUpInit,
GetChoices, Empty, DebugForm, Wait, SchedulesControllerInit,
SchedulesListInit, SchedulesList, ProjectUpdate, $state, CreateSelect2,
OrganizationList, NotificationsListInit, ToggleNotification) {
OrganizationList, NotificationsListInit, ToggleNotification, i18n) {
ClearScope('htmlTemplate');
@ -769,8 +771,8 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
$scope.$emit('projectLoaded');
})
.error(function (data, status) {
ProcessErrors($scope, data, status, form, { hdr: 'Error!',
msg: 'Failed to retrieve project: ' + id + '. GET status: ' + status
ProcessErrors($scope, data, status, form, { hdr: i18n._('Error!'),
msg: i18n._('Failed to retrieve project: ') + id + i18n._('. GET status: ') + status
});
});
});
@ -871,8 +873,8 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
};
Prompt({
hdr: 'Delete',
body: '<div class="Prompt-bodyQuery">Are you sure you want to remove the ' + title + ' below from ' + $scope.name + '?</div><div class="Prompt-bodyTarget">' + name + '</div>',
hdr: i18n._('Delete'),
body: i18n.format(i18n._('<div class="Prompt-bodyQuery">Are you sure you want to remove the %s below from %s?</div>'), title, $scope.name) + '<div class="Prompt-bodyTarget">' + name + '</div>',
action: action,
actionText: 'DELETE'
});
@ -889,27 +891,27 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
if($scope.scm_type.value) {
switch ($scope.scm_type.value) {
case 'git':
$scope.urlPopover = '<p>Example URLs for GIT SCM include:</p><ul class=\"no-bullets\"><li>https://github.com/ansible/ansible.git</li>' +
$scope.urlPopover = i18n._('<p>Example URLs for GIT SCM include:</p><ul class=\"no-bullets\"><li>https://github.com/ansible/ansible.git</li>' +
'<li>git@github.com:ansible/ansible.git</li><li>git://servername.example.com/ansible.git</li></ul>' +
'<p><strong>Note:</strong> When using SSH protocol for GitHub or Bitbucket, enter an SSH key only, ' +
'do not enter a username (other than git). Additionally, GitHub and Bitbucket do not support password authentication when using ' +
'SSH. GIT read only protocol (git://) does not use username or password information.';
'SSH. GIT read only protocol (git://) does not use username or password information.');
break;
case 'svn':
$scope.urlPopover = '<p>Example URLs for Subversion SCM include:</p>' +
$scope.urlPopover = i18n._('<p>Example URLs for Subversion SCM include:</p>' +
'<ul class=\"no-bullets\"><li>https://github.com/ansible/ansible</li><li>svn://servername.example.com/path</li>' +
'<li>svn+ssh://servername.example.com/path</li></ul>';
'<li>svn+ssh://servername.example.com/path</li></ul>');
break;
case 'hg':
$scope.urlPopover = '<p>Example URLs for Mercurial SCM include:</p>' +
$scope.urlPopover = i18n._('<p>Example URLs for Mercurial SCM include:</p>' +
'<ul class=\"no-bullets\"><li>https://bitbucket.org/username/project</li><li>ssh://hg@bitbucket.org/username/project</li>' +
'<li>ssh://server.example.com/path</li></ul>' +
'<p><strong>Note:</strong> Mercurial does not support password authentication for SSH. ' +
'Do not put the username and key in the URL. ' +
'If using Bitbucket and SSH, do not supply your Bitbucket username.';
'If using Bitbucket and SSH, do not supply your Bitbucket username.');
break;
default:
$scope.urlPopover = '<p> URL popover text';
$scope.urlPopover = i18n._('<p> URL popover text');
}
}
};
@ -918,7 +920,7 @@ export function ProjectsEdit($scope, $rootScope, $compile, $location, $log,
if ($scope.project_obj.scm_type === "Manual" || Empty($scope.project_obj.scm_type)) {
// ignore
} else if ($scope.project_obj.status === 'updating' || $scope.project_obj.status === 'running' || $scope.project_obj.status === 'pending') {
Alert('Update in Progress', 'The SCM update process is running.', 'alert-info');
Alert('Update in Progress', i18n._('The SCM update process is running.'), 'alert-info');
} else {
ProjectUpdate({ scope: $scope, project_id: $scope.project_obj.id });
}
@ -936,5 +938,5 @@ ProjectsEdit.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log',
'Authorization', 'CredentialList', 'LookUpInit', 'GetChoices', 'Empty',
'DebugForm', 'Wait', 'SchedulesControllerInit', 'SchedulesListInit',
'SchedulesList', 'ProjectUpdate', '$state', 'CreateSelect2',
'OrganizationList', 'NotificationsListInit', 'ToggleNotification'
'OrganizationList', 'NotificationsListInit', 'ToggleNotification', 'i18n'
];

View File

@ -10,10 +10,12 @@
* @description This controller's the Users page
*/
import {N_} from "../i18n";
const user_type_options = [
{type: 'normal' , label: 'Normal User' },
{type: 'system_auditor' , label: 'System Auditor' },
{type: 'system_administrator', label: 'System Administrator' },
{type: 'normal' , label: N_('Normal User') },
{type: 'system_auditor' , label: N_('System Auditor') },
{type: 'system_administrator', label: N_('System Administrator') },
];
function user_type_sync($scope) {
@ -34,7 +36,12 @@ function user_type_sync($scope) {
export function UsersList($scope, $rootScope, $location, $log, $stateParams,
Rest, Alert, UserList, GenerateList, Prompt, SearchInit, PaginateInit,
ReturnToCaller, ClearScope, ProcessErrors, GetBasePath, SelectionInit,
Wait, $state, Refresh, $filter, rbacUiControlService) {
Wait, $state, Refresh, $filter, rbacUiControlService, i18n) {
for (var i = 0; i < user_type_options.length; i++) {
user_type_options[i].label = i18n._(user_type_options[i].label);
}
ClearScope();
$scope.canAdd = false;
@ -142,7 +149,7 @@ UsersList.$inject = ['$scope', '$rootScope', '$location', '$log',
'$stateParams', 'Rest', 'Alert', 'UserList', 'generateList', 'Prompt',
'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope',
'ProcessErrors', 'GetBasePath', 'SelectionInit', 'Wait', '$state',
'Refresh', '$filter', 'rbacUiControlService'
'Refresh', '$filter', 'rbacUiControlService', 'i18n'
];
@ -152,7 +159,11 @@ UsersList.$inject = ['$scope', '$rootScope', '$location', '$log',
export function UsersAdd($scope, $rootScope, $compile, $location, $log,
$stateParams, UserForm, GenerateForm, Rest, Alert, ProcessErrors,
ReturnToCaller, ClearScope, GetBasePath, LookUpInit, OrganizationList,
ResetForm, Wait, CreateSelect2, $state) {
ResetForm, Wait, CreateSelect2, $state, i18n) {
for (var i = 0; i < user_type_options.length; i++) {
user_type_options[i].label = i18n._(user_type_options[i].label);
}
Rest.setUrl(GetBasePath('users'));
Rest.options()
@ -268,14 +279,19 @@ export function UsersAdd($scope, $rootScope, $compile, $location, $log,
UsersAdd.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log',
'$stateParams', 'UserForm', 'GenerateForm', 'Rest', 'Alert',
'ProcessErrors', 'ReturnToCaller', 'ClearScope', 'GetBasePath',
'LookUpInit', 'OrganizationList', 'ResetForm', 'Wait', 'CreateSelect2', '$state'
'LookUpInit', 'OrganizationList', 'ResetForm', 'Wait', 'CreateSelect2', '$state',
'i18n'
];
export function UsersEdit($scope, $rootScope, $location,
$stateParams, UserForm, GenerateForm, Rest, ProcessErrors,
RelatedSearchInit, RelatedPaginateInit, ClearScope,
GetBasePath, ResetForm, Wait, CreateSelect2 ,$state) {
GetBasePath, ResetForm, Wait, CreateSelect2 ,$state, i18n) {
for (var i = 0; i < user_type_options.length; i++) {
user_type_options[i].label = i18n._(user_type_options[i].label);
}
ClearScope();
@ -439,5 +455,5 @@ export function UsersEdit($scope, $rootScope, $location,
UsersEdit.$inject = ['$scope', '$rootScope', '$location',
'$stateParams', 'UserForm', 'GenerateForm', 'Rest', 'ProcessErrors',
'RelatedSearchInit', 'RelatedPaginateInit', 'ClearScope', 'GetBasePath',
'ResetForm', 'Wait', 'CreateSelect2', '$state'
'ResetForm', 'Wait', 'CreateSelect2', '$state', 'i18n'
];

View File

@ -1,7 +1,8 @@
/* jshint unused: vars */
export default
[ 'templateUrl',
function(templateUrl) {
'i18n',
function(templateUrl, i18n) {
return {
restrict: 'E',
scope: {
@ -35,34 +36,34 @@ export default
{
url: "/#/home/hosts",
number: scope.data.hosts.total,
label: "Hosts"
label: i18n._("Hosts")
},
{
url: "/#/home/hosts?active-failures=true",
number: scope.data.hosts.failed,
label: "Failed Hosts",
label: i18n._("Failed Hosts"),
isFailureCount: true
},
{
url: "/#/inventories",
number: scope.data.inventories.total,
label: "Inventories",
label: i18n._("Inventories"),
},
{
url: "/#/inventories?status=sync-failed",
number: scope.data.inventories.inventory_failed,
label: "Inventory Sync Failures",
label: i18n._("Inventory Sync Failures"),
isFailureCount: true
},
{
url: "/#/projects",
number: scope.data.projects.total,
label: "Projects"
label: i18n._("Projects")
},
{
url: "/#/projects?status=failed,canceled",
number: scope.data.projects.failed,
label: "Project Sync Failures",
label: i18n._("Project Sync Failures"),
isFailureCount: true
}
], function(val) { return addFailureToCount(val); });

View File

@ -1,68 +1,68 @@
<div class="DashboardGraphs">
<div class="DashboardGraphs-headerSection">
<h3 class="DashboardGraphs-headerText">
JOB STATUS
<translate>JOB STATUS</translate>
</h3>
<div class="DashboardGraphs-graphToolbar" ng-show="!hostStatusSelected">
<div class="DashboardGraphs-filteringDropdowns">
<div class="DashboardGraphs-filterLabel">Period</div>
<div class="DashboardGraphs-filterLabel" translate>Period</div>
<div class="DashboardGraphs-periodDropdown">
<a id="period-dropdown" role="button"
data-toggle="dropdown"
data-target="#"
href="/page.html"
class="DashboardGraphs-filterDropdownText">
Past Month <i class="fa fa-chevron-down DashboardGraphs-filterIcon"></i>
<translate>Past Month</translate> <i class="fa fa-chevron-down DashboardGraphs-filterIcon"></i>
</a>
<ul class="dropdown-menu DashboardGraphs-filterDropdownItems
DashboardGraphs-filterDropdownItems--period" role="menu" aria-labelledby="period-dropdown">
<li>
<a class="n" id="day" >Past 24 Hours </a>
<a class="n" id="day" translate>Past 24 Hours </a>
</li>
<li>
<a class="n" id="week">Past Week</a>
<a class="n" id="week" translate>Past Week</a>
</li>
<li>
<a class="n" id="month">Past Month</a>
<a class="n" id="month" translate>Past Month</a>
</li>
</ul>
</div>
</div>
<div class="DashboardGraphs-filteringDropdowns">
<div class="DashboardGraphs-filterLabel">Job Type</div>
<div class="DashboardGraphs-filterLabel" translate>Job Type</div>
<div class="DashboardGraphs-jobTypeDropdown">
<a id="type-dropdown" role="button" data-toggle="dropdown" data-target="#" class="DashboardGraphs-filterDropdownText"
href="/page.html">
All <i class="fa fa-chevron-down DashboardGraphs-filterIcon"></i>
<translate>All</translate> <i class="fa fa-chevron-down DashboardGraphs-filterIcon"></i>
</a>
<ul class="dropdown-menu DashboardGraphs-filterDropdownItems
DashboardGraphs-filterDropdownItems--jobType" role="menu" aria-labelledby="type-dropdown">
<li>
<a class="m" id="all">All</a>
<a class="m" id="all" translate>All</a>
</li>
<li>
<a class="m" id="inv_sync">Inventory Sync</a>
<a class="m" id="inv_sync" translate>Inventory Sync</a>
</li>
<li>
<a class="m" id="scm_update">SCM Update</a>
<a class="m" id="scm_update" translate>SCM Update</a>
</li>
<li>
<a class="m" id="playbook_run">Playbook Run</a>
<a class="m" id="playbook_run" translate>Playbook Run</a>
</li>
</ul>
</div>
</div>
<div class="DashboardGraphs-filteringDropdowns">
<div class="DashboardGraphs-filterLabel">View</div>
<div class="DashboardGraphs-filterLabel" translate>View</div>
<div class="DashboardGraphs-statusDropdown">
<a id="status-dropdown" role="button"
data-toggle="dropdown"
data-target="#"
href="/page.html"
class="DashboardGraphs-filterDropdownText">
All
<translate>All</translate>
<i class="fa fa-chevron-down
DashboardGraphs-filterIcon">
</i>
@ -71,13 +71,13 @@
<ul class="dropdown-menu DashboardGraphs-filterDropdownItems
DashboardGraphs-filterDropdownItems--status" role="menu" aria-labelledby="status-dropdown">
<li>
<a class="o" id="both" >All</a>
<a class="o" id="both" translate>All</a>
</li>
<li>
<a class="o" id="failed">Successful</a>
<a class="o" id="failed" translate>Successful</a>
</li>
<li>
<a class="o" id="successful">Failed</a>
<a class="o" id="successful" translate>Failed</a>
</li>
</ul>
</div>

View File

@ -13,10 +13,11 @@
'adjustGraphSize',
'jobStatusGraphData',
'templateUrl',
'i18n',
JobStatusGraph
];
function JobStatusGraph($rootScope, $compile , $location, $window, Wait, adjustGraphSize, graphDataService, templateUrl) {
function JobStatusGraph($rootScope, $compile , $location, $window, Wait, adjustGraphSize, graphDataService, templateUrl, i18n) {
return {
restrict: 'E',
scope: {
@ -60,10 +61,10 @@ function JobStatusGraph($rootScope, $compile , $location, $window, Wait, adjustG
var timeFormat, graphData = [
{ "color": "#5CB85C",
"key": "SUCCESSFUL",
"key": i18n._("SUCCESSFUL"),
"values": data.jobs.successful
},
{ "key" : "FAILED" ,
{ "key" : i18n._("FAILED") ,
"color" : "#D9534F",
"values": data.jobs.failed
}
@ -102,14 +103,14 @@ function JobStatusGraph($rootScope, $compile , $location, $window, Wait, adjustG
job_status_chart.interactiveLayer.tooltip.distance(-1); //distance from interactive line to tooltip
job_status_chart.xAxis
.axisLabel("TIME")//.showMaxMin(true)
.axisLabel(i18n._("TIME"))//.showMaxMin(true)
.tickFormat(function(d) {
var dx = graphData[0].values[d] && graphData[0].values[d].x || 0;
return dx ? d3.time.format(timeFormat)(new Date(Number(dx+'000'))) : '';
});
job_status_chart.yAxis //Chart y-axis settings
.axisLabel('JOBS')
.axisLabel(i18n._('JOBS'))
.tickFormat(d3.format('.f'));
d3.select(element.find('svg')[0])

View File

@ -5,17 +5,17 @@
*************************************************/
export default function(){
export default [ 'i18n', function(i18n){
return {
name: 'hosts',
iterator: 'host',
selectTitle: 'Add Existing Hosts',
selectTitle: i18n._('Add Existing Hosts'),
editTitle: 'Hosts',
listTitle: 'Hosts',
index: false,
hover: true,
well: true,
emptyListText: 'NO HOSTS FOUND',
emptyListText: i18n._('NO HOSTS FOUND'),
fields: {
status: {
basePath: 'unified_jobs',
@ -91,4 +91,4 @@ export default function(){
}
};
}
}];

View File

@ -1,23 +1,23 @@
<div class="DashboardList" ng-hide="noJobTemplates">
<div class="DashboardList-header">
<h3 class="DashboardList-headerText">
RECENTLY USED JOB TEMPLATES
<translate>RECENTLY USED JOB TEMPLATES</translate>
</h3>
<a href="/#/job_templates" class="DashboardList-viewAll">
VIEW ALL
<translate>VIEW ALL</translate>
</a>
</div>
<div class="DashboardList-container">
<table class="List-table">
<tr class="List-tableHeaderRow">
<th class="List-tableHeader DashboardList-tableHeader--name">
Name
<translate>Name</translate>
</th>
<th class="List-tableHeader DashboardList-tableHeader--activity">
Activity
<translate>Activity</translate>
</th>
<th class="List-tableHeader DashboardList-tableHeader--actions">
Actions
<translate>Actions</translate>
</th>
</tr>
<tr class="List-tableRow"
@ -49,11 +49,11 @@
<div class="DashboardList" ng-show="noJobTemplates">
<div class="DashboardList-header">
<h3 class="DashboardList-headerText">
RECENTLY USED JOB TEMPLATES
<translate>RECENTLY USED JOB TEMPLATES</translate>
</h3>
</div>
<div class="DashboardList-container">
<p class="DashboardList-noJobs">No job templates were recently used.<br />
<p class="DashboardList-noJobs" translate>No job templates were recently used.<br />
You can create a job template <a href="#/job_templates/add">here</a>.</p>
</div>
</div>

View File

@ -1,17 +1,17 @@
<div class="DashboardList" ng-hide="noJobs">
<div class="DashboardList-header">
<h3 class="DashboardList-headerText">
RECENT JOB RUNS
<translate>RECENT JOB RUNS</translate>
</h3>
<a href="/#/jobs" class="DashboardList-viewAll">
VIEW ALL
<translate>VIEW ALL</translate>
</a>
</div>
<div class="DashboardList-container">
<table class="List-table">
<tr>
<th class="List-tableHeader DashboardList-tableHeader--name">Name</th>
<th class="List-tableHeader DashboardList-tableHeader--time">Time</th>
<th class="List-tableHeader DashboardList-tableHeader--name" translate>Name</th>
<th class="List-tableHeader DashboardList-tableHeader--time" translate>Time</th>
</tr>
<tr class="List-tableRow"
ng-class-odd="'List-tableRow--oddRow'"
@ -39,10 +39,10 @@
<div class="DashboardList" ng-show="noJobs">
<div class="DashboardList-header">
<h3 class="DashboardList-headerText">
RECENTLY RUN JOBS
<translate>RECENTLY RUN JOBS</translate>
</h3>
</div>
<div class="DashboardList-container">
<p class="DashboardList-noJobs">No jobs were recently run.</p>
<p class="DashboardList-noJobs" translate>No jobs were recently run.</p>
</div>
</div>

View File

@ -12,14 +12,15 @@
export default
angular.module('CredentialFormDefinition', [])
.value('CredentialForm', {
.factory('CredentialForm', ['i18n', function(i18n) {
return {
addTitle: 'Create Credential', //Legend in add mode
addTitle: i18n._('Create Credential'), //Legend in add mode
editTitle: '{{ name }}', //Legend in edit mode
name: 'credential',
forceListeners: true,
subFormTitles: {
credentialSubForm: 'Type Details',
credentialSubForm: i18n._('Type Details'),
},
actions: {
@ -28,7 +29,7 @@ export default
fields: {
name: {
label: 'Name',
label: i18n._('Name'),
type: 'text',
addRequired: true,
editRequired: true,
@ -36,7 +37,7 @@ export default
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
},
description: {
label: 'Description',
label: i18n._('Description'),
type: 'text',
addRequired: false,
editRequired: false,
@ -46,26 +47,26 @@ export default
addRequired: false,
editRequired: false,
ngShow: 'canShareCredential',
label: 'Organization',
label: i18n._('Organization'),
type: 'lookup',
sourceModel: 'organization',
sourceField: 'name',
ngClick: 'lookUpOrganization()',
awPopOver: "<p>If no organization is given, the credential can only be used by the user that creates the credential. Organization admins and system administrators can assign an organization so that roles for the credential can be assigned to users and teams in that organization.</p>",
dataTitle: 'Organization ',
awPopOver: i18n._("<p>If no organization is given, the credential can only be used by the user that creates the credential. Organization admins and system administrators can assign an organization so that roles for the credential can be assigned to users and teams in that organization.</p>"),
dataTitle: i18n._('Organization') + ' ',
dataPlacement: 'bottom',
dataContainer: "body",
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
},
kind: {
label: 'Type',
label: i18n._('Type'),
excludeModal: true,
type: 'select',
ngOptions: 'kind.label for kind in credential_kind_options track by kind.value', // select as label for value in array 'kind.label for kind in credential_kind_options',
ngChange: 'kindChange()',
addRequired: true,
editRequired: true,
awPopOver:'<dl>\n' +
awPopOver: i18n._('<dl>\n' +
'<dt>Machine</dt>\n' +
'<dd>Authentication for remote machine access. This can include SSH keys, usernames, passwords, ' +
'and sudo information. Machine credentials are used when submitting jobs to run playbooks against ' +
@ -82,15 +83,15 @@ export default
'<dd>Usernames, passwords, and access keys for authenticating to the specified cloud or infrastructure ' +
'provider. These are used for dynamic inventory sources and for cloud provisioning and deployment ' +
'in playbook runs.</dd>\n' +
'</dl>\n',
dataTitle: 'Type',
'</dl>\n'),
dataTitle: i18n._('Type'),
dataPlacement: 'right',
dataContainer: "body",
hasSubForm: true,
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
},
access_key: {
label: 'Access Key',
label: i18n._('Access Key'),
type: 'text',
ngShow: "kind.value == 'aws'",
awRequiredWhen: {
@ -103,7 +104,7 @@ export default
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
},
secret_key: {
label: 'Secret Key',
label: i18n._('Secret Key'),
type: 'sensitive',
ngShow: "kind.value == 'aws'",
ngDisabled: "secret_key_ask || !(credential_obj.summary_fields.user_capabilities.edit || canAdd)",
@ -118,14 +119,14 @@ export default
subForm: 'credentialSubForm'
},
security_token: {
label: 'STS Token',
label: i18n._('STS Token'),
type: 'sensitive',
ngShow: "kind.value == 'aws'",
autocomplete: false,
apiField: 'security_token',
awPopOver: "<div>Security Token Service (STS) is a web service that enables you to request temporary, limited-privilege credentials for AWS Identity and Access Management (IAM) users.</div><div style='padding-top: 10px'>To learn more about the IAM STS Token, refer to the <a href='http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp.html' target='_blank'>Amazon documentation</a>.</div>",
awPopOver: i18n._("<div>Security Token Service (STS) is a web service that enables you to request temporary, limited-privilege credentials for AWS Identity and Access Management (IAM) users.</div><div style='padding-top: 10px'>To learn more about the IAM STS Token, refer to the <a href='http://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp.html' target='_blank'>Amazon documentation</a>.</div>"),
hasShowInputButton: true,
dataTitle: 'STS Token',
dataTitle: i18n._('STS Token'),
dataPlacement: 'right',
dataContainer: "body",
subForm: 'credentialSubForm',
@ -136,8 +137,8 @@ export default
type: 'text',
ngShow: "kind.value == 'vmware' || kind.value == 'openstack' || kind.value === 'satellite6' || kind.value === 'cloudforms'",
awPopOverWatch: "hostPopOver",
awPopOver: "set in helpers/credentials",
dataTitle: 'Host',
awPopOver: i18n._("set in helpers/credentials"),
dataTitle: i18n._('Host'),
dataPlacement: 'right',
dataContainer: "body",
autocomplete: false,
@ -149,7 +150,7 @@ export default
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
},
"subscription": {
label: "Subscription ID",
label: i18n._("Subscription ID"),
type: 'text',
ngShow: "kind.value == 'azure' || kind.value == 'azure_rm'",
awRequiredWhen: {
@ -159,8 +160,8 @@ export default
addRequired: false,
editRequired: false,
autocomplete: false,
awPopOver: '<p>Subscription ID is an Azure construct, which is mapped to a username.</p>',
dataTitle: 'Subscription ID',
awPopOver: i18n._('<p>Subscription ID is an Azure construct, which is mapped to a username.</p>'),
dataTitle: i18n._('Subscription ID'),
dataPlacement: 'right',
dataContainer: "body",
subForm: 'credentialSubForm',
@ -188,15 +189,15 @@ export default
init: false
},
autocomplete: false,
awPopOver: '<p>The email address assigned to the Google Compute Engine <b><i>service account.</b></i></p>',
dataTitle: 'Email',
awPopOver: i18n._('<p>The email address assigned to the Google Compute Engine <b><i>service account.</b></i></p>'),
dataTitle: i18n._('Email'),
dataPlacement: 'right',
dataContainer: "body",
subForm: 'credentialSubForm',
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
},
"api_key": {
label: 'API Key',
label: i18n._('API Key'),
type: 'sensitive',
ngShow: "kind.value == 'rax'",
awRequiredWhen: {
@ -224,7 +225,7 @@ export default
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
},
"ssh_password": {
label: 'Password',
label: i18n._('Password'),
type: 'sensitive',
ngShow: "kind.value == 'ssh'",
ngDisabled: "ssh_password_ask || !(credential_obj.summary_fields.user_capabilities.edit || canAdd)",
@ -232,7 +233,7 @@ export default
editRequired: false,
subCheckbox: {
variable: 'ssh_password_ask',
text: 'Ask at runtime?',
text: i18n._('Ask at runtime?'),
ngChange: 'ask(\'ssh_password\', \'undefined\')'
},
hasShowInputButton: true,
@ -254,16 +255,16 @@ export default
editRequired: false,
awDropFile: true,
rows: 10,
awPopOver: "SSH key description",
awPopOver: i18n._("SSH key description"),
awPopOverWatch: "key_description",
dataTitle: 'Private Key',
dataTitle: i18n._('Private Key'),
dataPlacement: 'right',
dataContainer: "body",
subForm: "credentialSubForm",
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
},
"ssh_key_unlock": {
label: 'Private Key Passphrase',
label: i18n._('Private Key Passphrase'),
type: 'sensitive',
ngShow: "kind.value == 'ssh' || kind.value == 'scm'",
addRequired: false,
@ -272,7 +273,7 @@ export default
subCheckbox: {
variable: 'ssh_key_unlock_ask',
ngShow: "kind.value == 'ssh'",
text: 'Ask at runtime?',
text: i18n._('Ask at runtime?'),
ngChange: 'ask(\'ssh_key_unlock\', \'undefined\')',
ngDisabled: "keyEntered === false"
},
@ -280,15 +281,15 @@ export default
subForm: 'credentialSubForm'
},
"become_method": {
label: "Privilege Escalation",
label: i18n._("Privilege Escalation"),
// hintText: "If your playbooks use privilege escalation (\"sudo: true\", \"su: true\", etc), you can specify the username to become, and the password to use here.",
type: 'select',
ngShow: "kind.value == 'ssh'",
dataTitle: 'Privilege Escalation',
dataTitle: i18n._('Privilege Escalation'),
ngOptions: 'become.label for become in become_options track by become.value',
awPopOver: "<p>Specify a method for 'become' operations. " +
awPopOver: i18n._("<p>Specify a method for 'become' operations. " +
"This is equivalent to specifying the <code>--become-method=BECOME_METHOD</code> parameter, where <code>BECOME_METHOD</code> could be "+
"<code>sudo | su | pbrun | pfexec | runas</code> <br>(defaults to <code>sudo</code>)</p>",
"<code>sudo | su | pbrun | pfexec | runas</code> <br>(defaults to <code>sudo</code>)</p>"),
dataPlacement: 'right',
dataContainer: "body",
subForm: 'credentialSubForm',
@ -313,7 +314,7 @@ export default
editRequired: false,
subCheckbox: {
variable: 'become_password_ask',
text: 'Ask at runtime?',
text: i18n._('Ask at runtime?'),
ngChange: 'ask(\'become_password\', \'undefined\')'
},
hasShowInputButton: true,
@ -322,7 +323,7 @@ export default
},
client:{
type: 'text',
label: 'Client ID',
label: i18n._('Client ID'),
subForm: 'credentialSubForm',
ngShow: "kind.value === 'azure_rm'",
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
@ -331,20 +332,20 @@ export default
type: 'sensitive',
hasShowInputButton: true,
autocomplete: false,
label: 'Client Secret',
label: i18n._('Client Secret'),
subForm: 'credentialSubForm',
ngShow: "kind.value === 'azure_rm'",
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
},
tenant: {
type: 'text',
label: 'Tenant ID',
label: i18n._('Tenant ID'),
subForm: 'credentialSubForm',
ngShow: "kind.value === 'azure_rm'",
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
},
authorize: {
label: 'Authorize',
label: i18n._('Authorize'),
type: 'checkbox',
ngChange: "toggleCallback('host_config_key')",
subForm: 'credentialSubForm',
@ -352,7 +353,7 @@ export default
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
},
authorize_password: {
label: 'Authorize Password',
label: i18n._('Authorize Password'),
type: 'sensitive',
hasShowInputButton: true,
autocomplete: false,
@ -365,8 +366,8 @@ export default
type: 'text',
ngShow: "kind.value == 'gce' || kind.value == 'openstack'",
awPopOverWatch: "projectPopOver",
awPopOver: "set in helpers/credentials",
dataTitle: 'Project Name',
awPopOver: i18n._("set in helpers/credentials"),
dataTitle: i18n._('Project Name'),
dataPlacement: 'right',
dataContainer: "body",
addRequired: false,
@ -382,12 +383,12 @@ export default
labelBind: 'domainLabel',
type: 'text',
ngShow: "kind.value == 'openstack'",
awPopOver: "<p>OpenStack domains define administrative " +
awPopOver: i18n._("<p>OpenStack domains define administrative " +
"boundaries. It is only needed for Keystone v3 authentication URLs. " +
"Common scenarios include:<ul><li><b>v2 URLs</b> - leave blank</li>" +
"<li><b>v3 default</b> - set to 'default'</br></li>" +
"<li><b>v3 multi-domain</b> - your domain name</p></li></ul></p>",
dataTitle: 'Domain Name',
"<li><b>v3 multi-domain</b> - your domain name</p></li></ul></p>"),
dataTitle: i18n._('Domain Name'),
dataPlacement: 'right',
dataContainer: "body",
addRequired: false,
@ -396,7 +397,7 @@ export default
ngDisabled: '!(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
},
"vault_password": {
label: "Vault Password",
label: i18n._("Vault Password"),
type: 'sensitive',
ngShow: "kind.value == 'ssh'",
ngDisabled: "vault_password_ask || !(credential_obj.summary_fields.user_capabilities.edit || canAdd)",
@ -404,7 +405,7 @@ export default
editRequired: false,
subCheckbox: {
variable: 'vault_password_ask',
text: 'Ask at runtime?',
text: i18n._('Ask at runtime?'),
ngChange: 'ask(\'vault_password\', \'undefined\')'
},
hasShowInputButton: true,
@ -438,7 +439,7 @@ export default
dataPlacement: 'top',
basePath: 'credentials/:id/access_list/',
type: 'collection',
title: 'Permissions',
title: i18n._('Permissions'),
iterator: 'permission',
index: false,
open: false,
@ -447,9 +448,9 @@ export default
add: {
ngClick: "addPermission",
label: 'Add',
awToolTip: 'Add a permission',
awToolTip: i18n._('Add a permission'),
actionClass: 'btn List-buttonSubmit',
buttonContent: '&#43; ADD',
buttonContent: i18n._('&#43; ADD'),
ngShow: '(credential_obj.summary_fields.user_capabilities.edit || canAdd)'
}
},
@ -457,19 +458,19 @@ export default
fields: {
username: {
key: true,
label: 'User',
label: i18n._('User'),
linkBase: 'users',
class: 'col-lg-3 col-md-3 col-sm-3 col-xs-4'
},
role: {
label: 'Role',
label: i18n._('Role'),
type: 'role',
noSort: true,
class: 'col-lg-4 col-md-4 col-sm-4 col-xs-4',
searchable: false
},
team_roles: {
label: 'Team Roles',
label: i18n._('Team Roles'),
type: 'team_roles',
noSort: true,
class: 'col-lg-5 col-md-5 col-sm-5 col-xs-4',
@ -487,4 +488,4 @@ export default
}
};
}
});
};}]);

View File

@ -12,9 +12,10 @@
export default
angular.module('InventoryFormDefinition', ['ScanJobsListDefinition'])
.value('InventoryFormObject', {
.factory('InventoryFormObject', ['i18n', function(i18n) {
return {
addTitle: 'New Inventory',
addTitle: i18n._('New Inventory'),
editTitle: '{{ inventory_name }}',
name: 'inventory',
tabs: true,
@ -22,7 +23,7 @@ export default
fields: {
inventory_name: {
realName: 'name',
label: 'Name',
label: i18n._('Name'),
type: 'text',
addRequired: true,
editRequired: true,
@ -31,14 +32,14 @@ export default
},
inventory_description: {
realName: 'description',
label: 'Description',
label: i18n._('Description'),
type: 'text',
addRequired: false,
editRequired: false,
ngDisabled: '!(inventory_obj.summary_fields.user_capabilities.edit || canAdd)'
},
organization: {
label: 'Organization',
label: i18n._('Organization'),
type: 'lookup',
sourceModel: 'organization',
sourceField: 'name',
@ -50,21 +51,21 @@ export default
ngDisabled: '!(inventory_obj.summary_fields.user_capabilities.edit || canAdd)'
},
variables: {
label: 'Variables',
label: i18n._('Variables'),
type: 'textarea',
class: 'Form-formGroup--fullWidth',
addRequired: false,
editRequird: false,
rows: 6,
"default": "---",
awPopOver: "<p>Enter inventory variables using either JSON or YAML syntax. Use the radio button to toggle between the two.</p>" +
awPopOver: i18n._("<p>Enter inventory variables using either JSON or YAML syntax. Use the radio button to toggle between the two.</p>" +
"JSON:<br />\n" +
"<blockquote>{<br />&emsp;\"somevar\": \"somevalue\",<br />&emsp;\"password\": \"magic\"<br /> }</blockquote>\n" +
"YAML:<br />\n" +
"<blockquote>---<br />somevar: somevalue<br />password: magic<br /></blockquote>\n" +
'<p>View JSON examples at <a href="http://www.json.org" target="_blank">www.json.org</a></p>' +
'<p>View YAML examples at <a href="http://docs.ansible.com/YAMLSyntax.html" target="_blank">docs.ansible.com</a></p>',
dataTitle: 'Inventory Variables',
'<p>View YAML examples at <a href="http://docs.ansible.com/YAMLSyntax.html" target="_blank">docs.ansible.com</a></p>'),
dataTitle: i18n._('Inventory Variables'),
dataPlacement: 'right',
dataContainer: 'body',
ngDisabled: '!(inventory_obj.summary_fields.user_capabilities.edit || canAdd)' // TODO: get working
@ -89,11 +90,11 @@ export default
related: {
permissions: {
awToolTip: 'Please save before assigning permissions',
awToolTip: i18n._('Please save before assigning permissions'),
dataPlacement: 'top',
basePath: 'inventories/:id/access_list/',
type: 'collection',
title: 'Permissions',
title: i18n._('Permissions'),
iterator: 'permission',
index: false,
open: false,
@ -102,9 +103,9 @@ export default
add: {
ngClick: "addPermission",
label: 'Add',
awToolTip: 'Add a permission',
awToolTip: i18n._('Add a permission'),
actionClass: 'btn List-buttonSubmit',
buttonContent: '&#43; ADD',
buttonContent: i18n._('&#43; ADD'),
ngShow: '(inventory_obj.summary_fields.user_capabilities.edit || canAdd)'
}
},
@ -112,19 +113,19 @@ export default
fields: {
username: {
key: true,
label: 'User',
label: i18n._('User'),
linkBase: 'users',
class: 'col-lg-3 col-md-3 col-sm-3 col-xs-4'
},
role: {
label: 'Role',
label: i18n._('Role'),
type: 'role',
noSort: true,
class: 'col-lg-4 col-md-4 col-sm-4 col-xs-4',
noSearch: true
},
team_roles: {
label: 'Team Roles',
label: i18n._('Team Roles'),
type: 'team_roles',
noSort: true,
class: 'col-lg-5 col-md-5 col-sm-5 col-xs-4',
@ -143,7 +144,7 @@ export default
};
}
})
};}])
.factory('InventoryForm', ['InventoryFormObject', 'ScanJobsList',
function(InventoryFormObject, ScanJobsList) {
return function() {

View File

@ -10,12 +10,14 @@
* @description This form is for adding/editing a Job Template
*/
export default
angular.module('JobTemplateFormDefinition', [ 'CompletedJobsDefinition'])
.value ('JobTemplateFormObject', {
.factory('JobTemplateFormObject', ['i18n', function(i18n) {
return {
addTitle: 'New Job Template',
addTitle: i18n._('New Job Template'),
editTitle: '{{ name }}',
name: 'job_templates',
base: 'job_templates',
@ -23,7 +25,7 @@ export default
fields: {
name: {
label: 'Name',
label: i18n._('Name'),
type: 'text',
addRequired: true,
editRequired: true,
@ -31,7 +33,7 @@ export default
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
},
description: {
label: 'Description',
label: i18n._('Description'),
type: 'text',
addRequired: false,
editRequired: false,
@ -39,7 +41,7 @@ export default
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
},
job_type: {
label: 'Job Type',
label: i18n._('Job Type'),
type: 'select',
ngOptions: 'type.label for type in job_type_options track by type.value',
ngChange: 'jobTypeChange()',
@ -47,22 +49,22 @@ export default
addRequired: true,
editRequired: true,
column: 1,
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 " +
" 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>",
dataTitle: 'Job Type',
" scanned facts for use with Tower's System Tracking feature.</p>"),
dataTitle: i18n._('Job Type'),
dataPlacement: 'right',
dataContainer: "body",
subCheckbox: {
variable: 'ask_job_type_on_launch',
ngShow: "!job_type.value || job_type.value !== 'scan'",
text: 'Prompt on launch'
text: i18n._('Prompt on launch')
},
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
},
inventory: {
label: 'Inventory',
label: i18n._('Inventory'),
type: 'lookup',
sourceModel: 'inventory',
sourceField: 'name',
@ -73,19 +75,19 @@ export default
},
requiredErrorMsg: "Please select an Inventory or check the Prompt on launch option.",
column: 1,
awPopOver: "<p>Select the inventory containing the hosts you want this job to manage.</p>",
dataTitle: 'Inventory',
awPopOver: i18n._("<p>Select the inventory containing the hosts you want this job to manage.</p>"),
dataTitle: i18n._('Inventory'),
dataPlacement: 'right',
dataContainer: "body",
subCheckbox: {
variable: 'ask_inventory_on_launch',
ngShow: "!job_type.value || job_type.value !== 'scan'",
text: 'Prompt on launch'
text: i18n._('Prompt on launch')
},
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
},
project: {
label: 'Project',
label: i18n._('Project'),
labelAction: {
label: 'RESET',
ngClick: 'resetProjectToDefault()',
@ -100,14 +102,14 @@ export default
init: "true"
},
column: 1,
awPopOver: "<p>Select the project containing the playbook you want this job to execute.</p>",
dataTitle: 'Project',
awPopOver: i18n._("<p>Select the project containing the playbook you want this job to execute.</p>"),
dataTitle: i18n._('Project'),
dataPlacement: 'right',
dataContainer: "body",
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
},
playbook: {
label: 'Playbook',
label: i18n._('Playbook'),
type:'select',
ngOptions: 'book for book in playbook_options track by book',
ngDisabled: "(job_type.value === 'scan' && project_name === 'Default') || !(job_template_obj.summary_fields.user_capabilities.edit || canAdd)",
@ -117,14 +119,14 @@ export default
init: "true"
},
column: 1,
awPopOver: "<p>Select the playbook to be executed by this job.</p>",
dataTitle: 'Playbook',
awPopOver: i18n._("<p>Select the playbook to be executed by this job.</p>"),
dataTitle: i18n._('Playbook'),
dataPlacement: 'right',
dataContainer: "body",
includePlaybookNotFoundError: true
},
credential: {
label: 'Machine Credential',
label: i18n._('Machine Credential'),
type: 'lookup',
sourceModel: 'credential',
sourceField: 'name',
@ -135,19 +137,19 @@ export default
},
requiredErrorMsg: "Please select a Machine Credential or check the Prompt on launch option.",
column: 1,
awPopOver: "<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>",
dataTitle: 'Credential',
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>"),
dataTitle: i18n._('Credential'),
dataPlacement: 'right',
dataContainer: "body",
subCheckbox: {
variable: 'ask_credential_on_launch',
text: 'Prompt on launch'
text: i18n._('Prompt on launch')
},
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
},
cloud_credential: {
label: 'Cloud Credential',
label: i18n._('Cloud Credential'),
type: 'lookup',
sourceModel: 'cloud_credential',
sourceField: 'name',
@ -155,15 +157,15 @@ export default
addRequired: false,
editRequired: false,
column: 1,
awPopOver: "<p>Selecting an optional cloud credential in the job template will pass along the access credentials to the " +
"running playbook, allowing provisioning into the cloud without manually passing parameters to the included modules.</p>",
dataTitle: 'Cloud Credential',
awPopOver: i18n._("<p>Selecting an optional cloud credential in the job template will pass along the access credentials to the " +
"running playbook, allowing provisioning into the cloud without manually passing parameters to the included modules.</p>"),
dataTitle: i18n._('Cloud Credential'),
dataPlacement: 'right',
dataContainer: "body",
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
},
network_credential: {
label: 'Network Credential',
label: i18n._('Network Credential'),
type: 'lookup',
sourceModel: 'network_credential',
sourceField: 'name',
@ -171,14 +173,14 @@ export default
addRequired: false,
editRequired: false,
column: 1,
awPopOver: "<p>Network credentials are used by Ansible networking modules to connect to and manage networking devices.</p>",
dataTitle: 'Network Credential',
awPopOver: i18n._("<p>Network credentials are used by Ansible networking modules to connect to and manage networking devices.</p>"),
dataTitle: i18n._('Network Credential'),
dataPlacement: 'right',
dataContainer: "body",
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
},
forks: {
label: 'Forks',
label: i18n._('Forks'),
id: 'forks-number',
type: 'number',
integer: true,
@ -189,121 +191,121 @@ export default
editRequired: false,
'class': "input-small",
column: 1,
awPopOver: '<p>The number of parallel or simultaneous processes to use while executing the playbook. 0 signifies ' +
awPopOver: i18n._('<p>The number of parallel or simultaneous processes to use while executing the playbook. 0 signifies ' +
'the default value from the <a id="ansible_forks_docs" href=\"http://docs.ansible.com/intro_configuration.html#the-ansible-configuration-file\" ' +
' target=\"_blank\">ansible configuration file</a>.</p>',
dataTitle: 'Forks',
' target=\"_blank\">ansible configuration file</a>.</p>'),
dataTitle: i18n._('Forks'),
dataPlacement: 'right',
dataContainer: "body",
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)' // TODO: get working
},
limit: {
label: 'Limit',
label: i18n._('Limit'),
type: 'text',
addRequired: false,
editRequired: false,
column: 1,
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 &#59; &#58; or &#44;</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>",
dataTitle: 'Limit',
"<a href=\"http://docs.ansible.com/intro_patterns.html\" target=\"_blank\">the Patterns topic at docs.ansible.com</a>.</p>"),
dataTitle: i18n._('Limit'),
dataPlacement: 'right',
dataContainer: "body",
subCheckbox: {
variable: 'ask_limit_on_launch',
text: 'Prompt on launch'
text: i18n._('Prompt on launch')
},
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
},
verbosity: {
label: 'Verbosity',
label: i18n._('Verbosity'),
type: 'select',
ngOptions: 'v.label for v in verbosity_options track by v.value',
"default": 1,
addRequired: true,
editRequired: true,
column: 1,
awPopOver: "<p>Control the level of output ansible will produce as the playbook executes.</p>",
dataTitle: 'Verbosity',
awPopOver: i18n._("<p>Control the level of output ansible will produce as the playbook executes.</p>"),
dataTitle: i18n._('Verbosity'),
dataPlacement: 'right',
dataContainer: "body",
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
},
job_tags: {
label: 'Job Tags',
label: i18n._('Job Tags'),
type: 'textarea',
rows: 5,
addRequired: false,
editRequired: false,
'elementClass': 'Form-textInput',
column: 2,
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>Consult the Ansible documentation for further details on the usage of tags.</p>",
dataTitle: "Job Tags",
"<p>Consult the Ansible documentation for further details on the usage of tags.</p>"),
dataTitle: i18n._("Job Tags"),
dataPlacement: "right",
dataContainer: "body",
subCheckbox: {
variable: 'ask_tags_on_launch',
text: 'Prompt on launch'
text: i18n._('Prompt on launch')
},
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
},
skip_tags: {
label: 'Skip Tags',
label: i18n._('Skip Tags'),
type: 'textarea',
rows: 5,
addRequired: false,
editRequired: false,
'elementClass': 'Form-textInput',
column: 2,
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>Consult the Ansible documentation for further details on the usage of tags.</p>",
dataTitle: "Skip Tags",
"<p>Consult the Ansible documentation for further details on the usage of tags.</p>"),
dataTitle: i18n._("Skip Tags"),
dataPlacement: "right",
dataContainer: "body",
subCheckbox: {
variable: 'ask_skip_tags_on_launch',
text: 'Prompt on launch'
text: i18n._('Prompt on launch')
},
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
},
checkbox_group: {
label: 'Options',
label: i18n._('Options'),
type: 'checkbox_group',
fields: [{
name: 'become_enabled',
label: 'Enable Privilege Escalation',
label: i18n._('Enable Privilege Escalation'),
type: 'checkbox',
addRequired: false,
editRequird: false,
column: 2,
awPopOver: "<p>If enabled, run this playbook as an administrator. This is the equivalent of passing the <code>--become</code> option to the <code>ansible-playbook</code> command. </p>",
awPopOver: i18n._("<p>If enabled, run this playbook as an administrator. This is the equivalent of passing the <code>--become</code> option to the <code>ansible-playbook</code> command. </p>"),
dataPlacement: 'right',
dataTitle: 'Become Privilege Escalation',
dataTitle: i18n._('Become Privilege Escalation'),
dataContainer: "body",
labelClass: 'stack-inline',
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
}, {
name: 'allow_callbacks',
label: 'Allow Provisioning Callbacks',
label: i18n._('Allow Provisioning Callbacks'),
type: 'checkbox',
addRequired: false,
editRequird: false,
ngChange: "toggleCallback('host_config_key')",
column: 2,
awPopOver: "<p>Enables creation of a provisioning callback URL. Using the URL a host can contact Tower and request a configuration update " +
"using this job template.</p>",
awPopOver: i18n._("<p>Enables creation of a provisioning callback URL. Using the URL a host can contact Tower and request a configuration update " +
"using this job template.</p>"),
dataPlacement: 'right',
dataTitle: 'Allow Provisioning Callbacks',
dataTitle: i18n._('Allow Provisioning Callbacks'),
dataContainer: "body",
labelClass: 'stack-inline',
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
}]
},
callback_url: {
label: 'Provisioning Callback URL',
label: i18n._('Provisioning Callback URL'),
type: 'text',
addRequired: false,
editRequired: false,
@ -313,12 +315,12 @@ export default
awPopOver: "callback_help",
awPopOverWatch: "callback_help",
dataPlacement: 'top',
dataTitle: 'Provisioning Callback URL',
dataTitle: i18n._('Provisioning Callback URL'),
dataContainer: "body",
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
},
host_config_key: {
label: 'Host Config Key',
label: i18n._('Host Config Key'),
type: 'text',
ngShow: "allow_callbacks && allow_callbacks !== 'false'",
ngChange: "configKeyChange()",
@ -327,26 +329,26 @@ export default
awPopOver: "callback_help",
awPopOverWatch: "callback_help",
dataPlacement: 'right',
dataTitle: "Host Config Key",
dataTitle: i18n._("Host Config Key"),
dataContainer: "body",
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
},
labels: {
label: 'Labels',
label: i18n._('Labels'),
type: 'select',
class: 'Form-formGroup--fullWidth',
ngOptions: 'label.label for label in labelOptions track by label.value',
multiSelect: true,
addRequired: false,
editRequired: false,
dataTitle: 'Labels',
dataTitle: i18n._('Labels'),
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',
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)'
},
variables: {
label: 'Extra Variables',
label: i18n._('Extra Variables'),
type: 'textarea',
class: 'Form-textAreaLabel Form-formGroup--fullWidth',
rows: 6,
@ -354,18 +356,18 @@ export default
editRequired: false,
"default": "---",
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>" +
"JSON:<br />\n" +
"<blockquote>{<br />&emsp;\"somevar\": \"somevalue\",<br />&emsp;\"password\": \"magic\"<br /> }</blockquote>\n" +
"YAML:<br />\n" +
"<blockquote>---<br />somevar: somevalue<br />password: magic<br /></blockquote>\n",
dataTitle: 'Extra Variables',
"<blockquote>---<br />somevar: somevalue<br />password: magic<br /></blockquote>\n"),
dataTitle: i18n._('Extra Variables'),
dataPlacement: 'right',
dataContainer: "body",
subCheckbox: {
variable: 'ask_variables_on_launch',
text: 'Prompt on launch'
text: i18n._('Prompt on launch')
},
ngDisabled: '!(job_template_obj.summary_fields.user_capabilities.edit || canAdd)' // TODO: get working
}
@ -409,11 +411,11 @@ export default
include: "CompletedJobsList"
},
permissions: {
awToolTip: 'Please save before assigning permissions',
awToolTip: i18n._('Please save before assigning permissions'),
dataPlacement: 'top',
basePath: 'job_templates/:id/access_list/',
type: 'collection',
title: 'Permissions',
title: i18n._('Permissions'),
iterator: 'permission',
index: false,
open: false,
@ -473,7 +475,7 @@ export default
}
};
}
})
};}])
.factory('JobTemplateForm', ['JobTemplateFormObject', 'NotificationsList', 'CompletedJobsList',
function(JobTemplateFormObject, NotificationsList, CompletedJobsList) {

View File

@ -12,16 +12,17 @@
export default
angular.module('OrganizationFormDefinition', [])
.value('OrganizationFormObject', {
.factory('OrganizationFormObject', ['i18n', function(i18n) {
return {
addTitle: 'New Organization', //Title in add mode
addTitle: i18n._('New Organization'), //Title in add mode
editTitle: '{{ name }}', //Title in edit mode
name: 'organization', //entity or model name in singular form
tabs: true,
fields: {
name: {
label: 'Name',
label: i18n._('Name'),
type: 'text',
addRequired: true,
editRequired: true,
@ -29,7 +30,7 @@ export default
ngDisabled: '!(organization_obj.summary_fields.user_capabilities.edit || canAdd)'
},
description: {
label: 'Description',
label: i18n._('Description'),
type: 'text',
addRequired: false,
editRequired: false,
@ -56,10 +57,10 @@ export default
related: {
permissions: {
basePath: 'organizations/:id/access_list/',
awToolTip: 'Please save before assigning permissions',
awToolTip: i18n._('Please save before assigning permissions'),
dataPlacement: 'top',
type: 'collection',
title: 'Permissions',
title: i18n._('Permissions'),
iterator: 'permission',
index: false,
open: false,
@ -68,9 +69,9 @@ export default
add: {
ngClick: "addPermission",
label: 'Add',
awToolTip: 'Add a permission',
awToolTip: i18n._('Add a permission'),
actionClass: 'btn List-buttonSubmit',
buttonContent: '&#43; ADD',
buttonContent: i18n._('&#43; ADD'),
ngShow: '(organization_obj.summary_fields.user_capabilities.edit || canAdd)'
}
},
@ -78,19 +79,19 @@ export default
fields: {
username: {
key: true,
label: 'User',
label: i18n._('User'),
linkBase: 'users',
class: 'col-lg-3 col-md-3 col-sm-3 col-xs-4'
},
role: {
label: 'Role',
label: i18n._('Role'),
type: 'role',
noSort: true,
class: 'col-lg-4 col-md-4 col-sm-4 col-xs-4',
searchable: false
},
team_roles: {
label: 'Team Roles',
label: i18n._('Team Roles'),
type: 'team_roles',
noSort: true,
class: 'col-lg-5 col-md-5 col-sm-5 col-xs-4',
@ -116,7 +117,7 @@ export default
}
};
}
})
};}])
.factory('OrganizationForm', ['OrganizationFormObject', 'NotificationsList',
function(OrganizationFormObject, NotificationsList) {

View File

@ -12,21 +12,22 @@
export default
angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
.value('ProjectsFormObject', {
.factory('ProjectsFormObject', ['i18n', function(i18n) {
return {
addTitle: 'New Project',
addTitle: i18n._('New Project'),
editTitle: '{{ name }}',
name: 'project',
forceListeners: true,
tabs: true,
subFormTitles: {
sourceSubForm: 'Source Details',
sourceSubForm: i18n._('Source Details'),
},
fields: {
name: {
label: 'Name',
label: i18n._('Name'),
type: 'text',
addRequired: true,
editRequired: true,
@ -34,14 +35,14 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
},
description: {
label: 'Description',
label: i18n._('Description'),
type: 'text',
addRequired: false,
editRequired: false,
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
},
organization: {
label: 'Organization',
label: i18n._('Organization'),
type: 'lookup',
sourceModel: 'organization',
sourceField: 'name',
@ -50,13 +51,13 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
reqExpression: "organizationrequired",
init: "true"
},
dataTitle: 'Organization',
dataTitle: i18n._('Organization'),
dataContainer: 'body',
dataPlacement: 'right',
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
},
scm_type: {
label: 'SCM Type',
label: i18n._('SCM Type'),
type: 'select',
class: 'Form-dropDown--scmType',
ngOptions: 'type.label for type in scm_type_options track by type.value',
@ -76,21 +77,21 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
closeable: false
},
base_dir: {
label: 'Project Base Path',
label: i18n._('Project Base Path'),
type: 'text',
class: 'Form-textUneditable',
showonly: true,
ngShow: "scm_type.value == 'manual' " ,
awPopOver: '<p>Base path used for locating playbooks. Directories found inside this path will be listed in the playbook directory drop-down. ' +
awPopOver: i18n._('<p>Base path used for locating playbooks. Directories found inside this path will be listed in the playbook directory drop-down. ' +
'Together the base path and selected playbook directory provide the full path used to locate playbooks.</p>' +
'<p>Use PROJECTS_ROOT in your environment settings file to determine the base path value.</p>',
dataTitle: 'Project Base Path',
'<p>Use PROJECTS_ROOT in your environment settings file to determine the base path value.</p>'),
dataTitle: i18n._('Project Base Path'),
dataContainer: 'body',
dataPlacement: 'right',
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
},
local_path: {
label: 'Playbook Directory',
label: i18n._('Playbook Directory'),
type: 'select',
id: 'local-path-select',
ngOptions: 'path.label for path in project_local_paths',
@ -99,10 +100,10 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
init: false
},
ngShow: "scm_type.value == 'manual' && !showMissingPlaybooksAlert",
awPopOver: '<p>Select from the list of directories found in the base path.' +
awPopOver: i18n._('<p>Select from the list of directories found in the base path.' +
'Together the base path and the playbook directory provide the full path used to locate playbooks.</p>' +
'<p>Use PROJECTS_ROOT in your environment settings file to determine the base path value.</p>',
dataTitle: 'Project Path',
'<p>Use PROJECTS_ROOT in your environment settings file to determine the base path value.</p>'),
dataTitle: i18n._('Project Path'),
dataContainer: 'body',
dataPlacement: 'right',
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
@ -134,7 +135,7 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
},
credential: {
label: 'SCM Credential',
label: i18n._('SCM Credential'),
type: 'lookup',
ngShow: "scm_type && scm_type.value !== 'manual'",
sourceModel: 'credential',
@ -146,43 +147,43 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
},
checkbox_group: {
label: 'SCM Update Options',
label: i18n._('SCM Update Options'),
type: 'checkbox_group',
ngShow: "scm_type && scm_type.value !== 'manual'",
subForm: 'sourceSubForm',
fields: [{
name: 'scm_clean',
label: 'Clean',
label: i18n._('Clean'),
type: 'checkbox',
addRequired: false,
editRequired: false,
awPopOver: '<p>Remove any local modifications prior to performing an update.</p>',
dataTitle: 'SCM Clean',
awPopOver: i18n._('<p>Remove any local modifications prior to performing an update.</p>'),
dataTitle: i18n._('SCM Clean'),
dataContainer: 'body',
dataPlacement: 'right',
labelClass: 'checkbox-options stack-inline',
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
}, {
name: 'scm_delete_on_update',
label: 'Delete on Update',
label: i18n._('Delete on Update'),
type: 'checkbox',
addRequired: false,
editRequired: false,
awPopOver: '<p>Delete the local repository in its entirety prior to performing an update.</p><p>Depending on the size of the ' +
'repository this may significantly increase the amount of time required to complete an update.</p>',
dataTitle: 'SCM Delete',
awPopOver: i18n._('<p>Delete the local repository in its entirety prior to performing an update.</p><p>Depending on the size of the ' +
'repository this may significantly increase the amount of time required to complete an update.</p>'),
dataTitle: i18n._('SCM Delete'),
dataContainer: 'body',
dataPlacement: 'right',
labelClass: 'checkbox-options stack-inline',
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)'
}, {
name: 'scm_update_on_launch',
label: 'Update on Launch',
label: i18n._('Update on Launch'),
type: 'checkbox',
addRequired: false,
editRequired: false,
awPopOver: '<p>Each time a job runs using this project, perform an update to the local repository prior to starting the job.</p>',
dataTitle: 'SCM Update',
awPopOver: i18n._('<p>Each time a job runs using this project, perform an update to the local repository prior to starting the job.</p>'),
dataTitle: i18n._('SCM Update'),
dataContainer: 'body',
dataPlacement: 'right',
labelClass: 'checkbox-options stack-inline',
@ -190,7 +191,7 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
}]
},
scm_update_cache_timeout: {
label: 'Cache Timeout<span class=\"small-text\"> (seconds)</span>',
label: i18n._('Cache Timeout<span class=\"small-text\"> (seconds)</span>'),
id: 'scm-cache-timeout',
type: 'number',
integer: true,
@ -200,10 +201,10 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
"default": '0',
addRequired: false,
editRequired: false,
awPopOver: '<p>Time in seconds to consider a project to be current. During job runs and callbacks the task system will ' +
awPopOver: i18n._('<p>Time in seconds to consider a project to be current. During job runs and callbacks the task system will ' +
'evaluate the timestamp of the latest project update. If it is older than Cache Timeout, it is not considered current, ' +
'and a new project update will be performed.</p>',
dataTitle: 'Cache Timeout',
'and a new project update will be performed.</p>'),
dataTitle: i18n._('Cache Timeout'),
dataPlacement: 'right',
dataContainer: "body",
ngDisabled: '!(project_obj.summary_fields.user_capabilities.edit || canAdd)' // TODO: get working
@ -228,11 +229,11 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
related: {
permissions: {
awToolTip: 'Please save before assigning permissions',
awToolTip: i18n._('Please save before assigning permissions'),
dataPlacement: 'top',
basePath: 'projects/:id/access_list/',
type: 'collection',
title: 'Permissions',
title: i18n._('Permissions'),
iterator: 'permission',
index: false,
open: false,
@ -241,9 +242,9 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
add: {
ngClick: "addPermission",
label: 'Add',
awToolTip: 'Add a permission',
awToolTip: i18n._('Add a permission'),
actionClass: 'btn List-buttonSubmit',
buttonContent: '&#43; ADD',
buttonContent: i18n._('&#43; ADD'),
ngShow: '(project_obj.summary_fields.user_capabilities.edit || canAdd)'
}
},
@ -289,9 +290,10 @@ angular.module('ProjectFormDefinition', ['SchedulesListDefinition'])
};
}
})
};}])
.factory('ProjectsForm', ['ProjectsFormObject', 'NotificationsList', function(ProjectsFormObject, NotificationsList) {
.factory('ProjectsForm', ['ProjectsFormObject', 'NotificationsList',
function(ProjectsFormObject, NotificationsList) {
return function() {
var itm;
for (itm in ProjectsFormObject.related) {

View File

@ -12,16 +12,17 @@
export default
angular.module('TeamFormDefinition', [])
.value('TeamForm', {
.factory('TeamForm', ['i18n', function(i18n) {
return {
addTitle: 'New Team', //Legend in add mode
addTitle: i18n._('New Team'), //Legend in add mode
editTitle: '{{ name }}', //Legend in edit mode
name: 'team',
tabs: true,
fields: {
name: {
label: 'Name',
label: i18n._('Name'),
type: 'text',
addRequired: true,
editRequired: true,
@ -29,14 +30,14 @@ export default
ngDisabled: '!(team_obj.summary_fields.user_capabilities.edit || canAdd)'
},
description: {
label: 'Description',
label: i18n._('Description'),
type: 'text',
addRequired: false,
editRequired: false,
ngDisabled: '!(team_obj.summary_fields.user_capabilities.edit || canAdd)'
},
organization: {
label: 'Organization',
label: i18n._('Organization'),
type: 'lookup',
sourceModel: 'organization',
sourceField: 'name',
@ -70,10 +71,10 @@ export default
related: {
access_list: {
dataPlacement: 'top',
awToolTip: 'Please save before adding users',
awToolTip: i18n._('Please save before adding users'),
basePath: 'teams/:id/access_list/',
type: 'collection',
title: 'Users',
title: i18n._('Users'),
iterator: 'permission',
index: false,
open: false,
@ -82,9 +83,9 @@ export default
add: {
ngClick: "addPermissionWithoutTeamTab",
label: 'Add',
awToolTip: 'Add user to team',
awToolTip: i18n._('Add user to team'),
actionClass: 'btn List-buttonSubmit',
buttonContent: '&#43; ADD',
buttonContent: i18n._('&#43; ADD'),
ngShow: '(team_obj.summary_fields.user_capabilities.edit || canAdd)'
}
},
@ -92,12 +93,12 @@ export default
fields: {
username: {
key: true,
label: 'User',
label: i18n._('User'),
linkBase: 'users',
class: 'col-lg-3 col-md-3 col-sm-3 col-xs-4'
},
role: {
label: 'Role',
label: i18n._('Role'),
type: 'role',
noSort: true,
class: 'col-lg-4 col-md-4 col-sm-4 col-xs-4',
@ -108,40 +109,40 @@ export default
roles: {
hideSearchAndActions: true,
dataPlacement: 'top',
awToolTip: 'Please save before assigning permissions',
awToolTip: i18n._('Please save before assigning permissions'),
basePath: 'teams/:id/roles/',
type: 'collection',
title: 'Granted Permissions',
title: i18n._('Granted Permissions'),
iterator: 'role',
open: false,
index: false,
actions: {},
emptyListText: 'No permissions have been granted',
emptyListText: i18n._('No permissions have been granted'),
fields: {
name: {
label: 'Name',
label: i18n._('Name'),
ngBind: 'role.summary_fields.resource_name',
linkTo: '{{convertApiUrl(role.related[role.summary_fields.resource_type])}}',
noSort: true
},
type: {
label: 'Type',
label: i18n._('Type'),
ngBind: 'role.summary_fields.resource_type_display_name',
noSort: true
},
role: {
label: 'Role',
label: i18n._('Role'),
ngBind: 'role.name',
noSort: true
}
},
fieldActions: {
"delete": {
label: 'Remove',
label: i18n._('Remove'),
ngClick: 'deletePermissionFromTeam(team_id, team_obj.name, role.name, role.summary_fields.resource_name, role.related.teams)',
'class': "List-actionButton--delete",
iconClass: 'fa fa-times',
awToolTip: 'Dissasociate permission from team',
awToolTip: i18n._('Dissasociate permission from team'),
dataPlacement: 'top',
ngShow: 'permission.summary_fields.user_capabilities.unattach'
}
@ -149,4 +150,4 @@ export default
hideOnSuperuser: true
}
},
}); //InventoryForm
};}]); //InventoryForm

View File

@ -12,9 +12,10 @@
export default
angular.module('UserFormDefinition', [])
.value('UserForm', {
.factory('UserForm', ['i18n', function(i18n) {
return {
addTitle: 'New User',
addTitle: i18n._('New User'),
editTitle: '{{ username }}',
name: 'user',
forceListeners: true,
@ -22,7 +23,7 @@ export default
fields: {
first_name: {
label: 'First Name',
label: i18n._('First Name'),
type: 'text',
addRequired: true,
editRequired: true,
@ -30,7 +31,7 @@ export default
ngDisabled: '!(user_obj.summary_fields.user_capabilities.edit || canAdd)'
},
last_name: {
label: 'Last Name',
label: i18n._('Last Name'),
type: 'text',
addRequired: true,
editRequired: true,
@ -38,7 +39,7 @@ export default
ngDisabled: '!(user_obj.summary_fields.user_capabilities.edit || canAdd)'
},
email: {
label: 'Email',
label: i18n._('Email'),
type: 'email',
addRequired: true,
editRequired: true,
@ -46,7 +47,7 @@ export default
ngDisabled: '!(user_obj.summary_fields.user_capabilities.edit || canAdd)'
},
username: {
label: 'Username',
label: i18n._('Username'),
type: 'text',
awRequiredWhen: {
reqExpression: "not_ldap_user && external_account === null",
@ -56,7 +57,7 @@ export default
ngDisabled: '!(user_obj.summary_fields.user_capabilities.edit || canAdd)'
},
organization: {
label: 'Organization',
label: i18n._('Organization'),
type: 'lookup',
sourceModel: 'organization',
sourceField: 'name',
@ -71,7 +72,7 @@ export default
ngDisabled: '!(user_obj.summary_fields.user_capabilities.edit || canAdd)'
},
password: {
label: 'Password',
label: i18n._('Password'),
type: 'sensitive',
hasShowInputButton: true,
ngShow: 'ldap_user == false && socialAuthUser === false && external_account === null',
@ -83,7 +84,7 @@ export default
ngDisabled: '!(user_obj.summary_fields.user_capabilities.edit || canAdd)'
},
password_confirm: {
label: 'Confirm Password',
label: i18n._('Confirm Password'),
type: 'sensitive',
hasShowInputButton: true,
ngShow: 'ldap_user == false && socialAuthUser === false && external_account === null',
@ -95,7 +96,7 @@ export default
ngDisabled: '!(user_obj.summary_fields.user_capabilities.edit || canAdd)'
},
user_type: {
label: 'User Type',
label: i18n._('User Type'),
type: 'select',
ngOptions: 'item as item.label for item in user_type_options track by item.type',
disableChooseOption: true,
@ -124,10 +125,10 @@ export default
related: {
organizations: {
basePath: 'users/:id/organizations',
awToolTip: 'Please save before assigning to organizations',
awToolTip: i18n._('Please save before assigning to organizations'),
dataPlacement: 'top',
type: 'collection',
title: 'Organizations',
title: i18n._('Organizations'),
iterator: 'organization',
index: false,
open: false,
@ -147,10 +148,10 @@ export default
},
teams: {
basePath: 'users/:id/teams',
awToolTip: 'Please save before assigning to teams',
awToolTip: i18n._('Please save before assigning to teams'),
dataPlacement: 'top',
type: 'collection',
title: 'Teams',
title: i18n._('Teams'),
iterator: 'team',
open: false,
index: false,
@ -168,39 +169,39 @@ export default
hideOnSuperuser: true
},
roles: {
awToolTip: 'Please save before assigning to organizations',
awToolTip: i18n._('Please save before assigning to organizations'),
dataPlacement: 'top',
hideSearchAndActions: true,
type: 'collection',
title: 'Granted permissions',
title: i18n._('Granted permissions'),
iterator: 'permission',
open: false,
index: false,
emptyListText: 'No permissions have been granted',
emptyListText: i18n._('No permissions have been granted'),
fields: {
name: {
label: 'Name',
label: i18n._('Name'),
ngBind: 'permission.summary_fields.resource_name',
linkTo: '{{convertApiUrl(permission.related[permission.summary_fields.resource_type])}}',
noSort: true
},
type: {
label: 'Type',
label: i18n._('Type'),
ngBind: 'permission.summary_fields.resource_type_display_name',
noSort: true
},
role: {
label: 'Role',
label: i18n._('Role'),
ngBind: 'permission.name',
noSort: true
},
},
fieldActions: {
"delete": {
label: 'Remove',
label: i18n._('Remove'),
ngClick: 'deletePermissionFromUser(user_id, username, permission.name, permission.summary_fields.resource_name, permission.related.users)',
iconClass: 'fa fa-times',
awToolTip: 'Dissasociate permission from user',
awToolTip: i18n._('Dissasociate permission from user'),
ngShow: 'permission.summary_fields.user_capabilities.unattach'
}
},
@ -208,4 +209,4 @@ export default
}
}
});
};}]);

View File

@ -13,8 +13,8 @@
export default
angular.module('CredentialsHelper', ['Utilities'])
.factory('KindChange', ['Empty',
function (Empty) {
.factory('KindChange', ['Empty', 'i18n',
function (Empty, i18n) {
return function (params) {
var scope = params.scope,
reset = params.reset,
@ -29,20 +29,20 @@ angular.module('CredentialsHelper', ['Utilities'])
$(this).hide();
});
// Put things in a default state
scope.usernameLabel = 'Username';
scope.usernameLabel = i18n._('Username');
scope.aws_required = false;
scope.email_required = false;
scope.rackspace_required = false;
scope.sshKeyDataLabel = 'Private Key';
scope.sshKeyDataLabel = i18n._('Private Key');
scope.username_required = false; // JT-- added username_required b/c mutliple 'kinds' need username to be required (GCE)
scope.key_required = false; // JT -- doing the same for key and project
scope.project_required = false;
scope.subscription_required = false;
scope.key_description = "Paste the contents of the SSH private key file.<div class=\"popover-footer\"><span class=\"key\">esc</span> or click to close</div>";
scope.key_description = i18n._("Paste the contents of the SSH private key file.<div class=\"popover-footer\"><span class=\"key\">esc</span> or click to close</div>");
scope.host_required = false;
scope.password_required = false;
scope.hostLabel = '';
scope.passwordLabel = 'Password';
scope.passwordLabel = i18n._('Password');
$('.popover').each(function() {
// remove lingering popover <div>. Seems to be a bug in TB3 RC1
@ -53,26 +53,26 @@ angular.module('CredentialsHelper', ['Utilities'])
$(this).hide();
});
// Put things in a default state
scope.usernameLabel = 'Username';
scope.usernameLabel = i18n._('Username');
scope.aws_required = false;
scope.email_required = false;
scope.rackspace_required = false;
scope.sshKeyDataLabel = 'Private Key';
scope.sshKeyDataLabel = i18n._('Private Key');
scope.username_required = false; // JT-- added username_required b/c mutliple 'kinds' need username to be required (GCE)
scope.key_required = false; // JT -- doing the same for key and project
scope.project_required = false;
scope.domain_required = false;
scope.subscription_required = false;
scope.key_description = "Paste the contents of the SSH private key file.";
scope.key_description = i18n._("Paste the contents of the SSH private key file.");
scope.host_required = false;
scope.password_required = false;
scope.hostLabel = '';
scope.projectLabel = '';
scope.domainLabel = '';
scope.project_required = false;
scope.passwordLabel = 'Password (API Key)';
scope.projectPopOver = "<p>The project value</p>";
scope.hostPopOver = "<p>The host value</p>";
scope.passwordLabel = i18n._('Password (API Key)');
scope.projectPopOver = i18n._("<p>The project value</p>");
scope.hostPopOver = i18n._("<p>The host value</p>");
scope.ssh_key_data_api_error = '';
if (!Empty(scope.kind)) {
// Apply kind specific settings
@ -85,38 +85,38 @@ angular.module('CredentialsHelper', ['Utilities'])
scope.username_required = true;
break;
case 'ssh':
scope.usernameLabel = 'Username'; //formally 'SSH Username'
scope.becomeUsernameLabel = 'Privilege Escalation Username';
scope.becomePasswordLabel = 'Privilege Escalation Password';
scope.usernameLabel = i18n._('Username'); //formally 'SSH Username'
scope.becomeUsernameLabel = i18n._('Privilege Escalation Username');
scope.becomePasswordLabel = i18n._('Privilege Escalation Password');
break;
case 'scm':
scope.sshKeyDataLabel = 'SCM Private Key';
scope.passwordLabel = 'Password';
scope.sshKeyDataLabel = i18n._('SCM Private Key');
scope.passwordLabel = i18n._('Password');
break;
case 'gce':
scope.usernameLabel = 'Service Account Email Address';
scope.sshKeyDataLabel = 'RSA Private Key';
scope.usernameLabel = i18n._('Service Account Email Address');
scope.sshKeyDataLabel = i18n._('RSA Private Key');
scope.email_required = true;
scope.key_required = true;
scope.project_required = true;
scope.key_description = 'Paste the contents of the PEM file associated with the service account email.';
scope.projectLabel = "Project";
scope.key_description = i18n._('Paste the contents of the PEM file associated with the service account email.');
scope.projectLabel = i18n._("Project");
scope.project_required = false;
scope.projectPopOver = "<p>The Project ID is the " +
scope.projectPopOver = i18n._("<p>The Project ID is the " +
"GCE assigned identification. It is constructed as " +
"two words followed by a three digit number. Such " +
"as: </p><p>adjective-noun-000</p>";
"as: </p><p>adjective-noun-000</p>");
break;
case 'azure':
scope.sshKeyDataLabel = 'Management Certificate';
scope.sshKeyDataLabel = i18n._('Management Certificate');
scope.subscription_required = true;
scope.key_required = true;
scope.key_description = "Paste the contents of the PEM file that corresponds to the certificate you uploaded in the Microsoft Azure console.";
scope.key_description = i18n._("Paste the contents of the PEM file that corresponds to the certificate you uploaded in the Microsoft Azure console.");
break;
case 'azure_rm':
scope.usernameLabel = "Username";
scope.usernameLabel = i18n._("Username");
scope.subscription_required = true;
scope.passwordLabel = 'Password';
scope.passwordLabel = i18n._('Password');
scope.azure_rm_required = true;
break;
case 'vmware':
@ -124,46 +124,46 @@ angular.module('CredentialsHelper', ['Utilities'])
scope.host_required = true;
scope.password_required = true;
scope.hostLabel = "vCenter Host";
scope.passwordLabel = 'Password';
scope.hostPopOver = "Enter the hostname or IP address which corresponds to your VMware vCenter.";
scope.passwordLabel = i18n._('Password');
scope.hostPopOver = i18n._("Enter the hostname or IP address which corresponds to your VMware vCenter.");
break;
case 'openstack':
scope.hostLabel = "Host (Authentication URL)";
scope.projectLabel = "Project (Tenant Name)";
scope.domainLabel = "Domain Name";
scope.hostLabel = i18n._("Host (Authentication URL)");
scope.projectLabel = i18n._("Project (Tenant Name)");
scope.domainLabel = i18n._("Domain Name");
scope.password_required = true;
scope.project_required = true;
scope.host_required = true;
scope.username_required = true;
scope.projectPopOver = "<p>This is the tenant name. " +
scope.projectPopOver = i18n._("<p>This is the tenant name. " +
" This value is usually the same " +
" as the username.</p>";
scope.hostPopOver = "<p>The host to authenticate with." +
"<br />For example, https://openstack.business.com/v2.0/";
" as the username.</p>");
scope.hostPopOver = i18n._("<p>The host to authenticate with." +
"<br />For example, https://openstack.business.com/v2.0/");
break;
case 'satellite6':
scope.username_required = true;
scope.password_required = true;
scope.passwordLabel = 'Password';
scope.passwordLabel = i18n._('Password');
scope.host_required = true;
scope.hostLabel = "Satellite 6 Host";
scope.hostPopOver = "Enter the hostname or IP address name which <br />" +
"corresponds to your Red Hat Satellite 6 server.";
scope.hostLabel = i18n._("Satellite 6 Host");
scope.hostPopOver = i18n._("Enter the hostname or IP address name which <br />" +
"corresponds to your Red Hat Satellite 6 server.");
break;
case 'cloudforms':
scope.username_required = true;
scope.password_required = true;
scope.passwordLabel = 'Password';
scope.passwordLabel = i18n._('Password');
scope.host_required = true;
scope.hostLabel = "CloudForms Host";
scope.hostPopOver = "Enter the hostname or IP address for the virtual <br />" +
" machine which is hosting the CloudForm appliance.";
scope.hostLabel = i18n._("CloudForms Host");
scope.hostPopOver = i18n._("Enter the hostname or IP address for the virtual <br />" +
" machine which is hosting the CloudForm appliance.");
break;
case 'net':
scope.username_required = true;
scope.password_required = false;
scope.passwordLabel = 'Password';
scope.sshKeyDataLabel = 'SSH Key';
scope.passwordLabel = i18n._('Password');
scope.sshKeyDataLabel = i18n._('SSH Key');
break;
}
}

100
awx/ui/client/src/i18n.js Normal file
View File

@ -0,0 +1,100 @@
function isString(arg) {
return typeof arg === 'string';
}
function isNull(arg) {
return arg === null;
}
function isObject(arg) {
return typeof arg === 'object' && arg !== null;
}
/**
* @ngdoc method
* @name function:i18n#N_
* @methodOf function:N_
* @description this function marks the translatable string with N_
* for 'grunt nggettext_extract'
*
*/
export function N_(s) {
return s;
}
// Copied format() from util/util.js. util.js includes "require()".
/**
* @ngdoc method
* @name function:i18n#format
* @methodOf function:format
* @description this function provides C-style's formatted sprintf().
*
*/
export function format(f) {
var i;
var formatRegExp = /%[sdj%]/g;
if (!isString(f)) {
var objects = [];
for (i = 0; i < arguments.length; i++) {
objects.push(JSON.stringify(arguments[i]));
}
return objects.join(' ');
}
i = 1;
var args = arguments;
var len = args.length;
var str = String(f).replace(formatRegExp, function(x) {
if (x === '%%') return '%';
if (i >= len) return x;
switch (x) {
case '%s': return String(args[i++]);
case '%d': return Number(args[i++]);
case '%j':
case '%j':
try {
return JSON.stringify(args[i++]);
} catch (_) {
return '[Circular]';
}
default:
return x;
}
});
for (var x = args[i]; i < len; x = args[++i]) {
if (isNull(x) || !isObject(x)) {
str += ' ' + x;
} else {
str += ' ' + JSON.stringify(x);
}
}
return str;
}
export default
angular.module('I18N', [])
.factory('I18NInit', ['$window', 'gettextCatalog',
function ($window, gettextCatalog) {
return function() {
var langInfo = $window.navigator.language ||
$window.navigator.userLanguage;
var langUrl = langInfo.replace('-', '_');
//gettextCatalog.debug = true;
gettextCatalog.setCurrentLanguage(langInfo);
// TODO: the line below is commented out temporarily until
// the .po files are received from the i18n team, in order to avoid
// 404 file not found console errors in dev
// gettextCatalog.loadRemote('/static/languages/' + langUrl + '.json');
};
}])
.factory('i18n', ['gettextCatalog',
function (gettextCatalog) {
return {
_: function (s) { return gettextCatalog.getString (s); },
N_: N_,
format: format,
hasTranslation: function () {
return gettextCatalog.strings[gettextCatalog.currentLanguage] !== undefined;
}
};
}]);

View File

@ -10,17 +10,17 @@
* @description This form is for adding/editing an organization
*/
export default function() {
export default ['i18n', function(i18n) {
return {
addTitle: 'New Custom Inventory',
addTitle: i18n._('New Custom Inventory'),
editTitle: '{{ name }}',
name: 'custom_inventory',
showActions: true,
fields: {
name: {
label: 'Name',
label: i18n._('Name'),
type: 'text',
addRequired: true,
editRequired: true,
@ -28,14 +28,14 @@ export default function() {
ngDisabled: '!(inventory_script_obj.summary_fields.user_capabilities.edit || canAdd)'
},
description: {
label: 'Description',
label: i18n._('Description'),
type: 'text',
addRequired: false,
editRequired: false,
ngDisabled: '!(inventory_script_obj.summary_fields.user_capabilities.edit || canAdd)'
},
organization: {
label: 'Organization',
label: i18n._('Organization'),
type: 'lookup',
awRequiredWhen: {
reqExpression: "orgrequired",
@ -47,7 +47,7 @@ export default function() {
ngDisabled: '!(inventory_script_obj.summary_fields.user_capabilities.edit || canAdd)'
},
script: {
label: 'Custom Script',
label: i18n._('Custom Script'),
type: 'textarea',
class: 'Form-formGroup--fullWidth',
elementClass: 'Form-monospace',
@ -56,9 +56,9 @@ export default function() {
awDropFile: true,
ngDisabled: '!(inventory_script_obj.summary_fields.user_capabilities.edit || canAdd)',
rows: 10,
awPopOver: "<p>Drag and drop your custom inventory script file here or create one in the field to import your custom inventory. " +
"<br><br> Script must begin with a hashbang sequence: i.e.... #!/usr/bin/env python</p>",
dataTitle: 'Custom Script',
awPopOver: i18n._("<p>Drag and drop your custom inventory script file here or create one in the field to import your custom inventory. " +
"<br><br> Script must begin with a hashbang sequence: i.e.... #!/usr/bin/env python</p>"),
dataTitle: i18n._('Custom Script'),
dataPlacement: 'right',
dataContainer: "body"
},
@ -80,4 +80,4 @@ export default function() {
}
}
};
}
}];

View File

@ -6,10 +6,10 @@
export default function(){
export default ['i18n', function(i18n){
return {
name: 'inventory_scripts' ,
listTitle: 'Inventory Scripts',
listTitle: i18n._('Inventory Scripts'),
iterator: 'inventory_script',
index: false,
hover: false,
@ -17,17 +17,17 @@ export default function(){
fields: {
name: {
key: true,
label: 'Name',
label: i18n._('Name'),
columnClass: 'col-md-3 col-sm-9 col-xs-9',
modalColumnClass: 'col-md-8'
},
description: {
label: 'Description',
label: i18n._('Description'),
excludeModal: true,
columnClass: 'col-md-4 hidden-sm hidden-xs'
},
organization: {
label: 'Organization',
label: i18n._('Organization'),
ngBind: 'inventory_script.summary_fields.organization.name',
sourceModel: 'organization',
sourceField: 'name',
@ -40,9 +40,9 @@ export default function(){
add: {
mode: 'all', // One of: edit, select, all
ngClick: 'addCustomInv()',
awToolTip: 'Create a new custom inventory',
awToolTip: i18n._('Create a new custom inventory'),
actionClass: 'btn List-buttonSubmit',
buttonContent: '&#43; ADD',
buttonContent: i18n._('&#43; ADD'),
ngShow: 'canAdd'
}
},
@ -54,29 +54,29 @@ export default function(){
edit: {
ngClick: "editCustomInv(inventory_script.id)",
icon: 'fa-edit',
label: 'Edit',
label: i18n._('Edit'),
"class": 'btn-sm',
awToolTip: 'Edit inventory script',
awToolTip: i18n._('Edit inventory script'),
dataPlacement: 'top',
ngShow: 'inventory_script.summary_fields.user_capabilities.edit'
},
view: {
ngClick: "editCustomInv(inventory_script.id)",
label: 'View',
label: i18n._('View'),
"class": 'btn-sm',
awToolTip: 'View inventory script',
awToolTip: i18n._('View inventory script'),
dataPlacement: 'top',
ngShow: '!inventory_script.summary_fields.user_capabilities.edit'
},
"delete": {
ngClick: "deleteCustomInv(inventory_script.id, inventory_script.name)",
icon: 'fa-trash',
label: 'Delete',
label: i18n._('Delete'),
"class": 'btn-sm',
awToolTip: 'Delete inventory script',
awToolTip: i18n._('Delete inventory script'),
dataPlacement: 'top',
ngShow: 'inventory_script.summary_fields.user_capabilities.delete'
}
}
};
}
}];

View File

@ -5,6 +5,7 @@
*************************************************/
import {templateUrl} from '../../shared/template-url/template-url.factory';
import {N_} from "../../i18n";
export default {
name: 'jobTemplates.add',
@ -13,7 +14,7 @@ export default {
controller: 'JobTemplatesAdd',
ncyBreadcrumb: {
parent: "jobTemplates",
label: "CREATE JOB TEMPLATE"
label: N_("CREATE JOB TEMPLATE")
},
socket:{
"groups":{

View File

@ -4,13 +4,15 @@
* All Rights Reserved
*************************************************/
import {N_} from "../i18n";
export default
['Wait', '$state', '$scope', '$rootScope', '$location', 'GetBasePath',
'Rest', 'ProcessErrors', 'CheckLicense', 'moment','$window',
'ConfigService', 'FeaturesService', 'pendoService',
'ConfigService', 'FeaturesService', 'pendoService', 'i18n',
function( Wait, $state, $scope, $rootScope, $location, GetBasePath, Rest,
ProcessErrors, CheckLicense, moment, $window, ConfigService,
FeaturesService, pendoService){
FeaturesService, pendoService, i18n){
$scope.getKey = function(event){
// Mimic HTML5 spec, show filename
$scope.fileName = event.target.files[0].name;
@ -90,8 +92,9 @@ export default
};
var init = function(){
$scope.fileName = "No file selected.";
$scope.title = $rootScope.licenseMissing ? "Tower License" : "License Management";
// license/license.partial.html compares fileName
$scope.fileName = N_("No file selected.");
$scope.title = $rootScope.licenseMissing ? ("Tower " + i18n._("License")) : i18n._("License Management");
Wait('start');
ConfigService.getConfig().then(function(config){
$scope.license = config;

View File

@ -2,91 +2,91 @@
ng-class="{'License-container--missing': licenseMissing}">
<div class="License-details" ng-hide="licenseMissing">
<div class="Panel">
<div class="License-titleText">Details</div>
<div class="License-titleText" translate>Details</div>
<div class="License-fields">
<div class="License-field">
<div class="License-field--label">License</div>
<div class="License-field--label" translate>License</div>
<div class="License-field--content">
<span class="License-greenText" ng-show='compliant'><i class="fa fa-circle License-greenText"></i>Valid License</span>
<span class="License-redText" ng-show='!compliant'><i class="fa fa-circle License-redText"></i>Invalid License</span>
<span class="License-greenText" ng-show='compliant'><i class="fa fa-circle License-greenText"></i><translate>Valid License</translate></span>
<span class="License-redText" ng-show='!compliant'><i class="fa fa-circle License-redText"></i><translate>Invalid License</transalte></span>
</div>
</div>
<div class="License-field">
<div class="License-field--label">Version</div>
<div class="License-field--label" translate>Version</div>
<div class="License-field--content">
{{license.version}}
</div>
</div>
<div class="License-field">
<div class="License-field--label">License Type</div>
<div class="License-field--label" translate>License Type</div>
<div class="License-field--content">
{{license.license_info.license_type}}
</div>
</div>
<div class="License-field">
<div class="License-field--label">Subscription</div>
<div class="License-field--label" translate>Subscription</div>
<div class="License-field--content">
{{license.license_info.subscription_name}}
</div>
</div>
<div class="License-field">
<div class="License-field--label">License Key</div>
<div class="License-field--label" translate>License Key</div>
<div class="License-field--content License-field--key">
{{license.license_info.license_key}}
</div>
</div>
<div class="License-field">
<div class="License-field--label">Expires On</div>
<div class="License-field--label" translate>Expires On</div>
<div class="License-field--content">
{{time.expiresOn}}
</div>
</div>
<div class="License-field">
<div class="License-field--label">Time Remaining</div>
<div class="License-field--label" translate>Time Remaining</div>
<div class="License-field--content"
ng-class="{'License-redText': time.remaining=='0 Days'}">
{{time.remaining}}
</div>
</div>
<div class="License-field">
<div class="License-field--label">Hosts Available</div>
<div class="License-field--label" translate>Hosts Available</div>
<div class="License-field--content">
{{license.license_info.available_instances}}
</div>
</div>
<div class="License-field">
<div class="License-field--label">Hosts Used</div>
<div class="License-field--label" translate>Hosts Used</div>
<div class="License-field--content">
{{license.license_info.current_instances}}
</div>
</div>
<div class="License-field License-greenText">
<div class="License-field--label">Hosts Remaining</div>
<div class="License-field--label" translate>Hosts Remaining</div>
<div class="License-field--content">
{{license.license_info.free_instances}}
</div>
</div>
</div>
<div class="License-upgradeText">If you are ready to upgrade, please contact us by clicking the button below</div>
<a href="https://www.ansible.com/renew" target="_blank"><button class="btn btn-primary">Upgrade</button></a>
<div class="License-upgradeText" translate>If you are ready to upgrade, please contact us by clicking the button below</div>
<a href="https://www.ansible.com/renew" target="_blank"><button class="btn btn-primary" translate>Upgrade</button></a>
</div>
</div>
<div class="License-management" ng-class="{'License-management--missingLicense' : licenseMissing}">
<div class="Panel">
<div class="License-titleText">{{title}}</div>
<div class="License-body">
<div class="License-helperText" ng-if="licenseMissing">Welcome to Ansible Tower! Please complete the steps below to acquire a license.</div>
<div class="License-helperText" ng-if="licenseMissing" translate>Welcome to Ansible Tower! Please complete the steps below to acquire a license.</div>
<div class="AddPermissions-directions" ng-if="licenseMissing">
<span class="AddPermissions-directionNumber">
1
</span>
<span class="License-helperText">
Please click the button below to visit Ansible's website to get a Tower license key.
<translate>Please click the button below to visit Ansible's website to get a Tower license key.</translate>
</span>
</div>
<button class="License-downloadLicenseButton btn btn-primary" ng-if="licenseMissing" ng-click="downloadLicense()">
Request License
<translate>Request License</translate>
</button>
<div class="AddPermissions-directions">
@ -94,18 +94,18 @@
2
</span>
<span class="License-helperText">
Choose your license file, agree to the End User License Agreement, and click submit.
<translate>Choose your license file, agree to the End User License Agreement, and click submit.</translate>
</span>
</div>
<form id="License-form" name="license">
<div class="License-subTitleText prepend-asterisk"> License File</div>
<div class="License-subTitleText prepend-asterisk"> <translate>License File</translate></div>
<div class="input-group License-file--container">
<span class="btn btn-primary" ng-click="fakeClick()">Browse</span>
<span class="License-fileName" ng-class="{'License-helperText' : fileName == 'No file selected.'}">{{fileName}}</span>
<span class="btn btn-primary" ng-click="fakeClick()" translate>Browse</span>
<span class="License-fileName" ng-class="{'License-helperText' : fileName == 'No file selected.'}">{{fileName|translate}}</span>
<input id="License-file" class="form-control" type="file" file-on-change="getKey"/>
</div>
<div class="License-subTitleText prepend-asterisk"> End User License Agreement
<div class="License-subTitleText prepend-asterisk"> <translate>End User License Agreement</translate>
</div>
<div id="eula_notice"
class="License-eulaNotice">{{ license.eula }}</div>
@ -113,13 +113,13 @@
<div class="checkbox">
<label class="License-details--label">
<input type="checkbox" ng-model="newLicense.eula" required>
I agree to the End User License Agreement
<translate>I agree to the End User License Agreement</translate>
</label>
</div>
</div>
<div class="License-submit--container">
<button ng-click="submit()" class="btn btn-success pull-right" ng-disabled="newLicense.file.license_key == null || newLicense.eula == null">Submit</button>
<span ng-show="success == true" class="License-greenText License-submit--success pull-right">Save successful!</span>
<button ng-click="submit()" class="btn btn-success pull-right" ng-disabled="newLicense.file.license_key == null || newLicense.eula == null" translate>Submit</button>
<span ng-show="success == true" class="License-greenText License-submit--success pull-right" translate>Save successful!</span>
</div>
</form>
</div>

View File

@ -5,22 +5,21 @@
*************************************************/
export default
angular.module('CompletedJobsDefinition', ['sanitizeFilter'])
.value( 'CompletedJobsList', {
.factory('CompletedJobsList', ['i18n', function(i18n) {
return {
// These tooltip fields are consumed to build disabled related tabs tooltips in the form > add view
awToolTip: 'Please save and run a job to view',
awToolTip: i18n._('Please save and run a job to view'),
dataPlacement: 'top',
name: 'completed_jobs',
basePath: 'job_templates/:id/jobs/?or__status=successful&or__status=failed&or__status=error&or__status=canceled',
iterator: 'completed_job',
editTitle: 'Completed Jobs',
editTitle: i18n._('Completed Jobs'),
index: false,
hover: true,
well: false,
emptyListText: 'No completed jobs',
emptyListText: i18n._('No completed jobs'),
fields: {
status: {
@ -53,7 +52,7 @@ export default
dataPlacement: 'top'
},
name: {
label: 'Name',
label: i18n._('Name'),
columnClass: 'col-lg-4 col-md-4 col-sm-4 col-xs-6',
searchable: false,
ngClick: "viewJobDetails(completed_job)",
@ -61,7 +60,7 @@ export default
dataPlacement: 'top'
},
type: {
label: 'Type',
label: i18n._('Type'),
ngBind: 'completed_job.type_label',
link: false,
columnClass: "col-lg-2 col-md-2 hidden-sm hidden-xs",
@ -70,7 +69,7 @@ export default
searchOptions: [] // populated via GetChoices() in controller
},
finished: {
label: 'Finished',
label: i18n._('Finished'),
noLink: true,
searchable: false,
filter: "longDate",
@ -79,7 +78,7 @@ export default
desc: true
},
failed: {
label: 'Job failed?',
label: i18n._('Job failed?'),
searchSingleValue: true,
searchType: 'boolean',
searchValue: 'true',
@ -110,4 +109,4 @@ export default
ngShow: 'completed_job.summary_fields.user_capabilities.delete'
}
}
});
};}]);

View File

@ -9,33 +9,34 @@
export default
angular.module('CredentialsListDefinition', [])
.value('CredentialList', {
.factory('CredentialList', ['i18n', function(i18n) {
return {
name: 'credentials',
iterator: 'credential',
selectTitle: 'Add Credentials',
editTitle: 'Credentials',
listTitle: 'Credentials',
selectTitle: i18n._('Add Credentials'),
editTitle: i18n._('Credentials'),
listTitle: i18n._('Credentials'),
selectInstructions: "<p>Select existing credentials by clicking each credential or checking the related checkbox. When " +
"finished, click the blue <em>Select</em> button, located bottom right.</p> <p>Create a brand new credential by clicking ",
index: false,
hover: true,
emptyListText: 'No Credentials Have Been Created',
emptyListText: i18n._('No Credentials Have Been Created'),
fields: {
name: {
key: true,
label: 'Name',
label: i18n._('Name'),
columnClass: 'col-md-3 col-sm-9 col-xs-9',
modalColumnClass: 'col-md-11'
},
description: {
label: 'Description',
label: i18n._('Description'),
excludeModal: true,
columnClass: 'col-md-3 hidden-sm hidden-xs'
},
kind: {
label: 'Type',
label: i18n._('Type'),
searchType: 'select',
searchOptions: [], // will be set by Options call to credentials resource
excludeModal: true,
@ -43,7 +44,7 @@ export default
columnClass: 'col-md-2 hidden-sm hidden-xs'
},
owners: {
label: 'Owners',
label: i18n._('Owners'),
type: 'owners',
searchable: false,
nosort: true,
@ -56,9 +57,9 @@ export default
add: {
mode: 'all', // One of: edit, select, all
ngClick: 'addCredential()',
awToolTip: 'Create a new credential',
awToolTip: i18n._('Create a new credential'),
actionClass: 'btn List-buttonSubmit',
buttonContent: '&#43; ADD'
buttonContent: i18n._('&#43; ADD')
}
},
@ -69,18 +70,18 @@ export default
edit: {
ngClick: "editCredential(credential.id)",
icon: 'fa-edit',
label: 'Edit',
label: i18n._('Edit'),
"class": 'btn-sm',
awToolTip: 'Edit credential',
awToolTip: i18n._('Edit credential'),
dataPlacement: 'top',
ngShow: 'credential.summary_fields.user_capabilities.edit'
},
view: {
ngClick: "editCredential(credential.id)",
label: 'View',
label: i18n._('View'),
"class": 'btn-sm',
awToolTip: 'View credential',
awToolTip: i18n._('View credential'),
dataPlacement: 'top',
ngShow: '!credential.summary_fields.user_capabilities.edit'
},
@ -88,11 +89,11 @@ export default
"delete": {
ngClick: "deleteCredential(credential.id, credential.name)",
icon: 'fa-trash',
label: 'Delete',
label: i18n._('Delete'),
"class": 'btn-sm',
awToolTip: 'Delete credential',
awToolTip: i18n._('Delete credential'),
dataPlacement: 'top',
ngShow: 'credential.summary_fields.user_capabilities.delete'
}
}
});
};}]);

View File

@ -7,13 +7,14 @@
export default
angular.module('InventoriesListDefinition', [])
.value('InventoryList', {
.factory('InventoryList', ['i18n', function(i18n) {
return {
name: 'inventories',
iterator: 'inventory',
selectTitle: 'Add Inventories',
editTitle: 'Inventories',
listTitle: 'Inventories',
selectTitle: i18n._('Add Inventories'),
editTitle: i18n._('Inventories'),
listTitle: i18n._('Inventories'),
selectInstructions: "Click on a row to select it, and click Finished when done. Click the <i class=\"icon-plus\"></i> " +
"button to create a new inventory.",
index: false,
@ -43,13 +44,13 @@ export default
},
name: {
key: true,
label: 'Name',
label: i18n._('Name'),
columnClass: 'col-md-5 col-sm-5 col-xs-8 List-staticColumnAdjacent',
modalColumnClass: 'col-md-11',
linkTo: '/#/inventories/{{inventory.id}}/manage'
},
organization: {
label: 'Organization',
label: i18n._('Organization'),
ngBind: 'inventory.summary_fields.organization.name',
linkTo: '/#/organizations/{{ inventory.organization }}',
sourceModel: 'organization',
@ -58,27 +59,27 @@ export default
columnClass: 'col-md-5 col-sm-3 hidden-xs'
},
has_inventory_sources: {
label: 'Cloud sourced?',
label: i18n._('Cloud sourced?'),
searchSingleValue: true,
searchType: 'boolean',
searchValue: 'true',
searchOnly: true
},
has_active_failures: {
label: 'Failed hosts?',
label: i18n._('Failed hosts?'),
searchSingleValue: true,
searchType: 'boolean',
searchValue: 'true',
searchOnly: true
},
inventory_sources_with_failures: {
label: 'Sync failures?',
label: i18n._('Sync failures?'),
searchType: 'select',
searchOptions: [{
label: 'Yes',
label: i18n._('Yes'),
value: 'inventory_sources_with_failures__gt=0'
}, {
label: 'No',
label: i18n._('No'),
value: 'inventory_sources_with_failures__lte=0'
}],
searchOnly: true
@ -89,9 +90,9 @@ export default
add: {
mode: 'all', // One of: edit, select, all
ngClick: 'addInventory()',
awToolTip: 'Create a new inventory',
awToolTip: i18n._('Create a new inventory'),
actionClass: 'btn List-buttonSubmit',
buttonContent: '&#43; ADD',
buttonContent: i18n._('&#43; ADD'),
ngShow: 'canAdd'
}
},
@ -101,25 +102,25 @@ export default
columnClass: 'col-md-2 col-sm-4 col-xs-4',
edit: {
label: 'Edit',
label: i18n._('Edit'),
ngClick: 'editInventory(inventory.id)',
awToolTip: 'Edit inventory',
awToolTip: i18n._('Edit inventory'),
dataPlacement: 'top',
ngShow: 'inventory.summary_fields.user_capabilities.edit'
},
view: {
label: 'View',
label: i18n._('View'),
ngClick: 'editInventory(inventory.id)',
awToolTip: 'View inventory',
awToolTip: i18n._('View inventory'),
dataPlacement: 'top',
ngShow: '!inventory.summary_fields.user_capabilities.edit'
},
"delete": {
label: 'Delete',
label: i18n._('Delete'),
ngClick: "deleteInventory(inventory.id, inventory.name)",
awToolTip: 'Delete inventory',
awToolTip: i18n._('Delete inventory'),
dataPlacement: 'top',
ngShow: 'inventory.summary_fields.user_capabilities.delete'
}
}
});
};}]);

View File

@ -7,13 +7,14 @@
export default
angular.module('JobTemplatesListDefinition', [])
.value('JobTemplateList', {
.factory('JobTemplateList', ['i18n', function(i18n) {
return {
name: 'job_templates',
iterator: 'job_template',
selectTitle: 'Add Job Template',
editTitle: 'Job Templates',
listTitle: 'Job Templates',
selectTitle: i18n._('Add Job Template'),
editTitle: i18n._('Job Templates'),
listTitle: i18n._('Job Templates'),
selectInstructions: "Click on a row to select it, and click Finished when done. Use the <i class=\"icon-plus\"></i> " +
"button to create a new job template.",
index: false,
@ -22,15 +23,15 @@ export default
fields: {
name: {
key: true,
label: 'Name',
label: i18n._('Name'),
columnClass: 'col-lg-2 col-md-2 col-sm-4 col-xs-9'
},
description: {
label: 'Description',
label: i18n._('Description'),
columnClass: 'col-lg-2 hidden-md hidden-sm hidden-xs'
},
smart_status: {
label: 'Activity',
label: i18n._('Activity'),
columnClass: 'List-tableCell col-lg-2 col-md-2 hidden-sm hidden-xs',
searchable: false,
nosort: true,
@ -38,7 +39,7 @@ export default
type: 'template'
},
labels: {
label: 'Labels',
label: i18n._('Labels'),
type: 'labels',
nosort: true,
columnClass: 'List-tableCell col-lg-4 col-md-4 hidden-sm hidden-xs',
@ -53,9 +54,9 @@ export default
mode: 'all', // One of: edit, select, all
ngClick: 'addJobTemplate()',
basePaths: ['job_templates'],
awToolTip: 'Create a new template',
awToolTip: i18n._('Create a new template'),
actionClass: 'btn List-buttonSubmit',
buttonContent: '&#43; ADD',
buttonContent: i18n._('&#43; ADD'),
ngShow: 'canAdd'
}
},
@ -65,52 +66,52 @@ export default
columnClass: 'col-lg-2 col-md-3 col-sm-3 col-xs-3',
submit: {
label: 'Launch',
label: i18n._('Launch'),
mode: 'all',
ngClick: 'submitJob(job_template.id)',
awToolTip: 'Start a job using this template',
awToolTip: i18n._('Start a job using this template'),
dataPlacement: 'top',
ngShow: 'job_template.summary_fields.user_capabilities.start'
},
schedule: {
label: 'Schedule',
label: i18n._('Schedule'),
mode: 'all',
ngClick: 'scheduleJob(job_template.id)',
awToolTip: 'Schedule future job template runs',
awToolTip: i18n._('Schedule future job template runs'),
dataPlacement: 'top',
ngShow: 'job_template.summary_fields.user_capabilities.schedule'
},
copy: {
label: 'Copy',
label: i18n._('Copy'),
'ui-sref': 'jobTemplates.copy({id: job_template.id})',
"class": 'btn-danger btn-xs',
awToolTip: 'Copy template',
awToolTip: i18n._('Copy template'),
dataPlacement: 'top',
ngShow: 'job_template.summary_fields.user_capabilities.copy'
},
edit: {
label: 'Edit',
label: i18n._('Edit'),
ngClick: "editJobTemplate(job_template.id)",
awToolTip: 'Edit template',
awToolTip: i18n._('Edit template'),
"class": 'btn-default btn-xs',
dataPlacement: 'top',
ngShow: 'job_template.summary_fields.user_capabilities.edit'
},
view: {
label: 'View',
label: i18n._('View'),
ngClick: "editJobTemplate(job_template.id)",
awToolTip: 'View template',
awToolTip: i18n._('View template'),
"class": 'btn-default btn-xs',
dataPlacement: 'top',
ngShow: '!job_template.summary_fields.user_capabilities.edit'
},
"delete": {
label: 'Delete',
label: i18n._('Delete'),
ngClick: "deleteJobTemplate(job_template.id, job_template.name)",
"class": 'btn-danger btn-xs',
awToolTip: 'Delete template',
awToolTip: i18n._('Delete template'),
dataPlacement: 'top',
ngShow: 'job_template.summary_fields.user_capabilities.delete'
}
}
});
};}]);

View File

@ -7,26 +7,27 @@
export default
angular.module('PortalJobTemplatesListDefinition', [])
.value('PortalJobTemplateList', {
.factory('PortalJobTemplateList', ['i18n', function(i18n) {
return {
name: 'job_templates',
iterator: 'job_template',
editTitle: 'Job Templates',
listTitle: 'Job Templates',
editTitle: i18n._('Job Templates'),
listTitle: i18n._('Job Templates'),
index: false,
hover: true,
well: true,
emptyListText: 'There are no job templates to display at this time',
emptyListText: i18n._('There are no job templates to display at this time'),
fields: {
name: {
key: true,
label: 'Name',
label: i18n._('Name'),
columnClass: 'col-lg-5 col-md-5 col-sm-9 col-xs-8',
linkTo: '/#/job_templates/{{job_template.id}}',
searchDefault: true
},
description: {
label: 'Description',
label: i18n._('Description'),
columnClass: 'col-lg-4 col-md-4 hidden-sm hidden-xs'
}
},
@ -36,11 +37,11 @@ export default
fieldActions: {
submit: {
label: 'Launch',
label: i18n._('Launch'),
mode: 'all',
ngClick: 'submitJob(job_template.id)',
awToolTip: 'Start a job using this template',
awToolTip: i18n._('Start a job using this template'),
dataPlacement: 'top'
}
}
});
};}]);

View File

@ -7,16 +7,17 @@
export default
angular.module('PortalJobsListDefinition', [])
.value( 'PortalJobsList', {
.factory('PortalJobsList', ['i18n', function(i18n) {
return {
name: 'jobs',
iterator: 'job',
editTitle: 'Jobs',
editTitle: i18n._('Jobs'),
index: false,
hover: true,
well: true,
listTitle: 'Jobs',
emptyListText: 'There are no jobs to display at this time',
listTitle: i18n._('Jobs'),
emptyListText: i18n._('There are no jobs to display at this time'),
fields: {
status: {
@ -32,14 +33,14 @@ export default
searchLabel: 'Status'
},
name: {
label: 'Name',
label: i18n._('Name'),
columnClass: 'col-lg-4 col-md-4 col-sm-4 col-xs-6 List-staticColumnAdjacent',
defaultSearchField: true,
linkTo: '/#/jobs/{{job.id}}',
searchDefault: true
},
finished: {
label: 'Finished',
label: i18n._('Finished'),
noLink: true,
searchable: false,
filter: "longDate",
@ -50,4 +51,4 @@ export default
},
actions: { }
});
};}]);

View File

@ -4,16 +4,16 @@
* All Rights Reserved
*************************************************/
export default
angular.module('ProjectsListDefinition', [])
.value('ProjectList', {
.factory('ProjectList', ['i18n', function(i18n) {
return {
name: 'projects',
iterator: 'project',
selectTitle: 'Add Project',
editTitle: 'Projects',
listTitle: 'Projects',
selectTitle: i18n._('Add Project'),
editTitle: i18n._('Projects'),
listTitle: i18n._('Projects'),
selectInstructions: '<p>Select existing projects by clicking each project or checking the related checkbox. When finished, click the blue ' +
'<em>Select</em> button, located bottom right.</p><p>Create a new project by clicking the <i class=\"fa fa-plus\"></i> button.</p>',
index: false,
@ -38,19 +38,19 @@ export default
name: {
key: true,
searchDefault: true,
label: 'Name',
label: i18n._('Name'),
columnClass: "col-lg-4 col-md-4 col-sm-5 col-xs-7 List-staticColumnAdjacent",
modalColumnClass: 'col-md-8'
},
scm_type: {
label: 'Type',
label: i18n._('Type'),
searchType: 'select',
searchOptions: [], // will be set by Options call to projects resource
excludeModal: true,
columnClass: 'col-lg-3 col-md-2 col-sm-3 hidden-xs'
},
last_updated: {
label: 'Last Updated',
label: i18n._('Last Updated'),
filter: "longDate",
columnClass: "col-lg-3 col-md-3 hidden-sm hidden-xs",
excludeModal: true,
@ -63,18 +63,18 @@ export default
add: {
mode: 'all', // One of: edit, select, all
ngClick: 'addProject()',
awToolTip: 'Create a new project',
awToolTip: i18n._('Create a new project'),
actionClass: 'btn List-buttonSubmit',
buttonContent: '&#43; ADD',
buttonContent: i18n._('&#43; ADD'),
ngShow: "canAdd"
},
refresh: {
mode: 'all',
awToolTip: "Refresh the page",
awToolTip: i18n._("Refresh the page"),
ngClick: "refresh()",
ngShow: "socketStatus == 'error'",
actionClass: 'btn List-buttonDefault',
buttonContent: 'REFRESH'
buttonContent: i18n._('REFRESH')
}
},
@ -100,28 +100,28 @@ export default
},
edit: {
ngClick: "editProject(project.id)",
awToolTip: 'Edit the project',
awToolTip: i18n._('Edit the project'),
dataPlacement: 'top',
ngShow: "project.summary_fields.user_capabilities.edit"
},
view: {
ngClick: "editProject(project.id)",
awToolTip: 'View the project',
awToolTip: i18n._('View the project'),
dataPlacement: 'top',
ngShow: "!project.summary_fields.user_capabilities.edit",
icon: 'fa-eye',
},
"delete": {
ngClick: "deleteProject(project.id, project.name)",
awToolTip: 'Delete the project',
awToolTip: i18n._('Delete the project'),
ngShow: "(project.status !== 'updating' && project.status !== 'running' && project.status !== 'pending') && project.summary_fields.user_capabilities.delete",
dataPlacement: 'top'
},
cancel: {
ngClick: "cancelUpdate(project.id, project.name)",
awToolTip: 'Cancel the SCM update',
awToolTip: i18n._('Cancel the SCM update'),
ngShow: "(project.status == 'updating' || project.status == 'running' || project.status == 'pending') && project.summary_fields.user_capabilities.start",
dataPlacement: 'top'
}
}
});
};}]);

View File

@ -7,14 +7,15 @@
export default
angular.module('ScheduledJobsDefinition', ['sanitizeFilter'])
.value( 'ScheduledJobsList', {
.factory('ScheduledJobsList', ['i18n', function(i18n) {
return {
name: 'schedules',
iterator: 'schedule',
editTitle: 'Scheduled Jobs',
editTitle: i18n._('Scheduled Jobs'),
hover: true,
well: false,
emptyListText: 'No schedules exist',
emptyListText: i18n._('No schedules exist'),
fields: {
enabled: {
@ -29,7 +30,7 @@ export default
dataPlacement: 'top'
},
name: {
label: 'Name',
label: i18n._('Name'),
columnClass: 'col-lg-4 col-md-5 col-sm-5 col-xs-7 List-staticColumnAdjacent',
sourceModel: 'unified_job_template',
sourceField: 'name',
@ -40,7 +41,7 @@ export default
defaultSearchField: true
},
type: {
label: 'Type',
label: i18n._('Type'),
noLink: true,
columnClass: "col-lg-2 col-md-2 hidden-sm hidden-xs",
sourceModel: 'unified_job_template',
@ -52,15 +53,15 @@ export default
searchable: true,
searchType: 'select',
searchOptions: [
{ value: 'inventorysource', label: 'Inventory Sync' },
{ value: 'jobtemplate', label: 'Playbook Run' },
{ value: 'project', label: 'SCM Update' },
{ value: 'systemjobtemplate', label: 'Management Job'}
{ value: 'inventorysource', label: i18n._('Inventory Sync') },
{ value: 'jobtemplate', label: i18n._('Playbook Run') },
{ value: 'project', label: i18n._('SCM Update') },
{ value: 'systemjobtemplate', label: i18n._('Management Job')}
]
},
next_run: {
label: 'Next Run',
label: i18n._('Next Run'),
noLink: true,
searchable: false,
columnClass: "col-lg-3 col-md-2 col-sm-3 hidden-xs",
@ -77,23 +78,23 @@ export default
"edit": {
mode: "all",
ngClick: "editSchedule(schedule)",
awToolTip: "Edit the schedule",
awToolTip: i18n._("Edit the schedule"),
dataPlacement: "top",
ngShow: 'schedule.summary_fields.user_capabilities.edit'
},
"view": {
mode: "all",
ngClick: "editSchedule(schedule)",
awToolTip: "View the schedule",
awToolTip: i18n._("View the schedule"),
dataPlacement: "top",
ngShow: '!schedule.summary_fields.user_capabilities.edit'
},
"delete": {
mode: 'all',
ngClick: 'deleteSchedule(schedule.id)',
awToolTip: 'Delete the schedule',
awToolTip: i18n._('Delete the schedule'),
dataPlacement: 'top',
ngShow: 'schedule.summary_fields.user_capabilities.delete'
}
}
});
};}]);

View File

@ -7,13 +7,14 @@
export default
angular.module('TeamsListDefinition', [])
.value('TeamList', {
.factory('TeamList', ['i18n', function(i18n) {
return {
name: 'teams',
iterator: 'team',
selectTitle: 'Add Team',
editTitle: 'Teams',
listTitle: 'Teams',
selectTitle: i18n._('Add Team'),
editTitle: i18n._('Teams'),
listTitle: i18n._('Teams'),
selectInstructions: "Click on a row to select it, and click Finished when done. Click the <i class=\"icon-plus\"></i> " +
"button to create a new team.",
index: false,
@ -22,17 +23,17 @@ export default
fields: {
name: {
key: true,
label: 'Name',
label: i18n._('Name'),
columnClass: 'col-lg-3 col-md-4 col-sm-9 col-xs-9',
modalColumnClass: 'col-md-8'
},
description: {
label: 'Description',
label: i18n._('Description'),
columnClass: 'col-lg-3 col-md-3 hidden-sm hidden-xs',
excludeModal: true
},
organization: {
label: 'Organization',
label: i18n._('Organization'),
ngBind: 'team.organization_name',
sourceModel: 'organization',
sourceField: 'name',
@ -45,9 +46,9 @@ export default
add: {
mode: 'all', // One of: edit, select, all
ngClick: 'addTeam()',
awToolTip: 'Create a new team',
awToolTip: i18n._('Create a new team'),
actionClass: 'btn List-buttonSubmit',
buttonContent: '&#43; ADD',
buttonContent: i18n._('&#43; ADD'),
ngShow: 'canAdd'
}
},
@ -57,30 +58,30 @@ export default
columnClass: 'col-lg-3 col-md-2 col-sm-3 col-xs-3',
edit: {
label: 'Edit',
label: i18n._('Edit'),
ngClick: "editTeam(team.id)",
icon: 'icon-edit',
"class": 'btn-xs btn-default',
awToolTip: 'Edit team',
awToolTip: i18n._('Edit team'),
dataPlacement: 'top',
ngShow: 'team.summary_fields.user_capabilities.edit'
},
view: {
label: 'View',
label: i18n._('View'),
ngClick: "editTeam(team.id)",
"class": 'btn-xs btn-default',
awToolTip: 'View team',
awToolTip: i18n._('View team'),
dataPlacement: 'top',
ngShow: '!team.summary_fields.user_capabilities.edit'
},
"delete": {
label: 'Delete',
label: i18n._('Delete'),
ngClick: "deleteTeam(team.id, team.name)",
icon: 'icon-trash',
"class": 'btn-xs btn-danger',
awToolTip: 'Delete team',
awToolTip: i18n._('Delete team'),
dataPlacement: 'top',
ngShow: 'team.summary_fields.user_capabilities.delete'
}
}
});
};}]);

View File

@ -7,13 +7,14 @@
export default
angular.module('UserListDefinition', [])
.value('UserList', {
.factory('UserList', ['i18n', function(i18n) {
return {
name: 'users',
iterator: 'user',
selectTitle: 'Add Users',
editTitle: 'Users',
listTitle: 'Users',
selectTitle: i18n._('Add Users'),
editTitle: i18n._('Users'),
listTitle: i18n._('Users'),
selectInstructions: '<p>Select existing users by clicking each user or checking the related checkbox. When finished, click the blue ' +
'<em>Select</em> button, located bottom right.</p> <p>When available, a brand new user can be created by clicking the ' +
'<i class=\"fa fa-plus\"></i> button.</p>',
@ -23,15 +24,15 @@ export default
fields: {
username: {
key: true,
label: 'Username',
label: i18n._('Username'),
columnClass: 'col-md-3 col-sm-3 col-xs-9'
},
first_name: {
label: 'First Name',
label: i18n._('First Name'),
columnClass: 'col-md-3 col-sm-3 hidden-xs'
},
last_name: {
label: 'Last Name',
label: i18n._('Last Name'),
columnClass: 'col-md-3 col-sm-3 hidden-xs'
}
},
@ -42,9 +43,9 @@ export default
mode: 'all', // One of: edit, select, all
ngClick: 'addUser()',
basePaths: ['organizations', 'users'], // base path must be in list, or action not available
awToolTip: 'Create a new user',
awToolTip: i18n._('Create a new user'),
actionClass: 'btn List-buttonSubmit',
buttonContent: '&#43; ADD',
buttonContent: i18n._('&#43; ADD'),
ngShow: 'canAdd'
}
},
@ -54,32 +55,32 @@ export default
columnClass: 'col-md-3 col-sm-3 col-xs-3',
edit: {
label: 'Edit',
label: i18n._('Edit'),
ngClick: "editUser(user.id)",
icon: 'icon-edit',
"class": 'btn-xs btn-default',
awToolTip: 'Edit user',
awToolTip: i18n._('Edit user'),
dataPlacement: 'top',
ngShow: 'user.summary_fields.user_capabilities.edit'
},
view: {
label: 'View',
label: i18n._('View'),
ngClick: "editUser(user.id)",
"class": 'btn-xs btn-default',
awToolTip: 'View user',
awToolTip: i18n._('View user'),
dataPlacement: 'top',
ngShow: '!user.summary_fields.user_capabilities.edit'
},
"delete": {
label: 'Delete',
label: i18n._('Delete'),
ngClick: "deleteUser(user.id, user.username)",
icon: 'icon-trash',
"class": 'btn-xs btn-danger',
awToolTip: 'Delete user',
awToolTip: i18n._('Delete user'),
dataPlacement: 'top',
ngShow: 'user.summary_fields.user_capabilities.delete'
}
}
});
};}]);

View File

@ -10,7 +10,7 @@
ng-src="/static/assets/{{ customLogo }}" >
</div>
<div class="LoginModal-body">
<div class="LoginModal-alert" ng-show="!sessionExpired && !sessionLimitExpired && !attemptFailed && !thirdPartyAttemptFailed">
<div class="LoginModal-alert" ng-show="!sessionExpired && !sessionLimitExpired && !attemptFailed && !thirdPartyAttemptFailed" translate>
Welcome to Ansible Tower! &nbsp;Please sign in.
</div>
<div class="LoginModal-alert LoginModal-alert--error" ng-show="sessionExpired">
@ -43,7 +43,7 @@
autocomplete="off" novalidate>
<div class="form-group LoginModal-formGroup">
<label class="LoginModal-label
col-md-12">USERNAME
col-md-12" translate>USERNAME
</label>
<div class="col-md-12">
<input type="text" name="login_username"
@ -62,7 +62,7 @@
</div>
<div class="form-group LoginModal-formGroup">
<label class="LoginModal-label
col-md-12">PASSWORD
col-md-12" translate>PASSWORD
</label>
<div class="col-md-12">
<input type="password"
@ -92,7 +92,7 @@
LoginModal-footerBlock--submit">
<button ng-click="systemLogin(login_username, login_password)"
id="login-button"
class="btn LoginModal-signInButton">
class="btn LoginModal-signInButton" translate>
SIGN IN
</button>
</div>

View File

@ -16,7 +16,7 @@
href="/#/projects"
ng-class="{'is-currentRoute' : isCurrentState('projects')}">
<span class="MainMenu-itemText">
PROJECTS
<translate>PROJECTS</translate>
</span>
</a>
<a class="MainMenu-item"
@ -24,7 +24,7 @@
href="/#/inventories"
ng-class="{'is-currentRoute' : isCurrentState('inventories')}">
<span class="MainMenu-itemText">
INVENTORIES
<translate>INVENTORIES</translate>
</span>
</a>
<a class="MainMenu-item"
@ -32,7 +32,7 @@
href="/#/job_templates"
ng-class="{'is-currentRoute' : isCurrentState('jobTemplates')}">
<span class="MainMenu-itemText">
JOB TEMPLATES
<translate>JOB TEMPLATES</translate>
</span>
</a>
<a class="MainMenu-item"
@ -40,7 +40,7 @@
href="/#/jobs"
ng-class="{'is-currentRoute' : isCurrentState('jobs')}">
<span class="MainMenu-itemText">
JOBS
<translate>JOBS</translate>
</span>
</a>
<a class="MainMenu-item"
@ -48,7 +48,7 @@
ng-href="/#/users/{{ $root.current_user.id }}"
ng-class="{'is-currentRoute' : isCurrentState('users.edit')}">
<span class="MainMenu-itemText">
VIEW USER PAGE FOR {{ $root.current_user.username | uppercase }}
<translate>VIEW USER PAGE FOR {{ $root.current_user.username | uppercase }}</translate>
</span>
</a>
<a class="MainMenu-item"
@ -56,7 +56,7 @@
ng-href="/#/setup"
ng-class="{'is-currentRoute' : isCurrentState('setup')}">
<span class="MainMenu-itemText">
SETTINGS
<translate>SETTINGS</translate>
</span>
</a>
<a class="MainMenu-item"
@ -64,7 +64,7 @@
ui-sref="portalMode"
ng-class="{'is-currentRoute' : isCurrentState('portalMode')}">
<span class="MainMenu-itemText">
PORTAL MODE
<translate>PORTAL MODE</translate>
</span>
</a>
<a class="MainMenu-item"
@ -72,7 +72,7 @@
ng-href="http://docs.ansible.com/ansible-tower/"
target="_blank">
<span class="MainMenu-itemText">
VIEW DOCUMENTATION
<translate>VIEW DOCUMENTATION</translate>
</span>
</a>
<a class="MainMenu-item"
@ -80,7 +80,7 @@
ng-href="/#/logout"
ng-class="{'is-currentRoute' : isCurrentState('logout')}">
<span class="MainMenu-itemText">
LOG OUT
<translate>LOG OUT</translate>
</span>
</a>
</span>
@ -92,7 +92,7 @@
ng-hide="licenseMissing"
ng-class="{'is-currentRoute' : isCurrentState('projects'), 'is-loggedOut' : !current_user || !current_user.username}">
<span class="MainMenu-itemText">
PROJECTS
<translate>PROJECTS</translate>
</span>
</a>
<a class="MainMenu-item MainMenu-item--notMobile MainMenu-item--left"
@ -101,7 +101,7 @@
ng-hide="licenseMissing"
ng-class="{'is-currentRoute' : isCurrentState('inventories'), 'is-loggedOut' : !current_user || !current_user.username}">
<span class="MainMenu-itemText">
INVENTORIES
<translate>INVENTORIES</translate>
</span>
</a>
<a class="MainMenu-item MainMenu-item--notMobile MainMenu-item--left"
@ -110,7 +110,7 @@
ng-hide="licenseMissing"
ng-class="{'is-currentRoute' : isCurrentState('jobTemplates'), 'is-loggedOut' : !current_user || !current_user.username}">
<span class="MainMenu-itemText">
JOB TEMPLATES
<translate>JOB TEMPLATES</translate>
</span>
</a>
<a class="MainMenu-item MainMenu-item--notMobile MainMenu-item--left MainMenu-item--lastLeft"
@ -119,7 +119,7 @@
ng-hide="licenseMissing"
ng-class="{'is-currentRoute' : isCurrentState('jobs'), 'is-loggedOut' : !current_user || !current_user.username}">
<span class="MainMenu-itemText">
JOBS
<translate>JOBS</translate>
</span>
</a>
<a class="MainMenu-item MainMenu-item--notMobile MainMenu-item--user MainMenu-item--right"
@ -144,7 +144,7 @@
ng-href="/#/setup"
ng-hide="licenseMissing"
ng-class="{'is-currentRoute' : isCurrentState('setup'), 'is-loggedOut' : !current_user || !current_user.username}"
aw-tool-tip="Settings"
aw-tool-tip="{{'Settings'|translate}}"
data-placement="bottom"
data-trigger="hover"
data-container="body">
@ -157,7 +157,7 @@
ng-href="/#/portal"
ng-hide="licenseMissing"
ng-class="{'is-currentRoute' : isCurrentState('portalMode'), 'is-loggedOut' : !current_user || !current_user.username}"
aw-tool-tip="My View"
aw-tool-tip="{{'My View'|translate}}"
data-placement="bottom"
data-trigger="hover"
data-container="body">
@ -170,7 +170,7 @@
ng-href="http://docs.ansible.com/ansible-tower/"
ng-hide="licenseMissing"
ng-class="{'is-loggedOut' : !current_user || !current_user.username}"
aw-tool-tip="View Documentation"
aw-tool-tip="{{'View Documentation'|translate}}"
data-placement="bottom"
data-trigger="hover"
data-container="body"
@ -185,7 +185,7 @@
ng-class="{'is-currentRoute' : isCurrentState('logout'),
'is-loggedOut' : !current_user || !current_user.username,
'MainMenu-item--licenseMissing' : licenseMissing}"
aw-tool-tip="Log Out"
aw-tool-tip="{{'Log Out'|translate}}"
data-placement="bottom"
data-trigger="hover"
data-container="body"

View File

@ -1,7 +1,7 @@
<div ui-view></div>
<div class="tab-pane Panel" id="management_jobs">
<div class="List-title">
<div class="List-titleText">
<div class="List-titleText" translate>
Management Jobs
</div>
<span class="badge List-titleBadge ng-binding">
@ -18,18 +18,18 @@
<button class="MgmtCards-actionItem List-actionButton"
ng-click='chooseRunJob(card.id, card.name)'
ng-show='current_user.is_superuser'
data-placement="top" aw-tool-tip="Launch Management Job" data-original-title="" title="">
data-placement="top" aw-tool-tip="{{'Launch Management Job'|translate}}" data-original-title="" title="">
<i class="MgmtCards-actionItemIcon icon-launch"></i>
</button>
<button class="MgmtCards-actionItem List-actionButton"
ng-click='configureSchedule(card.id)'
data-placement="top" aw-tool-tip="Schedule Management Job" data-original-title="" title="">
data-placement="top" aw-tool-tip="{{'Schedule Management Job'|translate}}" data-original-title="" title="">
<i class="MgmtCards-actionItemIcon fa fa-calendar"></i>
</button>
<button class="MgmtCards-actionItem List-actionButton"
ng-click='goToNotifications(card, card.id)'
ng-show='current_user.is_superuser'
data-placement="top" aw-tool-tip="Configure Notifications" data-original-title="" title="" ng-class="{'List-editButton--selected': activeCard === card.id && cardAction === 'notifications'}">
data-placement="top" aw-tool-tip="{{'Configure Notifications'|translate}}" data-original-title="" title="" ng-class="{'List-editButton--selected': activeCard === card.id && cardAction === 'notifications'}">
<i class="MgmtCards-actionItemIcon fa fa-bell-o"></i>
</button>
</div>

View File

@ -10,20 +10,20 @@
* @description This form is for adding/editing an organization
*/
export default function() {
export default ['i18n', function(i18n) {
return {
addTitle: 'New Notification Template',
addTitle: i18n._('New Notification Template'),
editTitle: '{{ name }}',
name: 'notification_template',
showActions: true,
subFormTitles: {
typeSubForm: 'Type Details',
typeSubForm: i18n._('Type Details'),
},
fields: {
name: {
label: 'Name',
label: i18n._('Name'),
type: 'text',
addRequired: true,
editRequired: true,
@ -31,14 +31,14 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
description: {
label: 'Description',
label: i18n._('Description'),
type: 'text',
addRequired: false,
editRequired: false,
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
organization: {
label: 'Organization',
label: i18n._('Organization'),
type: 'lookup',
sourceModel: 'organization',
sourceField: 'name',
@ -50,7 +50,7 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
notification_type: {
label: 'Type',
label: i18n._('Type'),
type: 'select',
addRequired: true,
editRequired: true,
@ -61,7 +61,7 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
username: {
label: 'Username',
label: i18n._('Username'),
type: 'text',
ngShow: "notification_type.value == 'email' ",
subForm: 'typeSubForm',
@ -69,7 +69,7 @@ export default function() {
},
host: {
label: 'Host',
label: i18n._('Host'),
type: 'text',
awRequiredWhen: {
reqExpression: "email_required",
@ -80,7 +80,7 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
sender: {
label: 'Sender Email',
label: i18n._('Sender Email'),
type: 'text',
awRequiredWhen: {
reqExpression: "email_required",
@ -91,12 +91,12 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
recipients: {
label: 'Recipient List',
label: i18n._('Recipient List'),
type: 'textarea',
rows: 3,
awPopOver: '<p>Type an option on each line.</p>'+
'<p>For example:<br>alias1@email.com<br>\n alias2@email.com<br>\n',
dataTitle: 'Recipient List',
awPopOver: i18n._('<p>Type an option on each line.</p>'+
'<p>For example:<br>alias1@email.com<br>\n alias2@email.com<br>\n'),
dataTitle: i18n._('Recipient List'),
dataPlacement: 'right',
dataContainer: "body",
awRequiredWhen: {
@ -135,12 +135,12 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
channels: {
label: 'Destination Channels',
label: i18n._('Destination Channels'),
type: 'textarea',
rows: 3,
awPopOver: '<p>Type an option on each line. The pound symbol (#) is not required.</p>'+
'<p>For example:<br>engineering<br>\n #support<br>\n',
dataTitle: 'Destination Channels',
awPopOver: i18n._('<p>Type an option on each line. The pound symbol (#) is not required.</p>'+
'<p>For example:<br>engineering<br>\n #support<br>\n'),
dataTitle: i18n._('Destination Channels'),
dataPlacement: 'right',
dataContainer: "body",
awRequiredWhen: {
@ -152,12 +152,12 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
rooms: {
label: 'Destination Channels',
label: i18n._('Destination Channels'),
type: 'textarea',
rows: 3,
awPopOver: '<p>Type an option on each line. The pound symbol (#) is not required.</p>'+
'<p>For example:<br>engineering<br>\n #support<br>\n',
dataTitle: 'Destination Channels',
awPopOver: i18n._('<p>Type an option on each line. The pound symbol (#) is not required.</p>'+
'<p>For example:<br>engineering<br>\n #support<br>\n'),
dataTitle: i18n._('Destination Channels'),
dataPlacement: 'right',
dataContainer: "body",
awRequiredWhen: {
@ -181,7 +181,7 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
account_token: {
label: 'Account Token',
label: i18n._('Account Token'),
type: 'sensitive',
hasShowInputButton: true,
awRequiredWhen: {
@ -193,10 +193,10 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
from_number: {
label: 'Source Phone Number',
label: i18n._('Source Phone Number'),
type: 'text',
awPopOver: '<p>Number associated with the "Messaging Service" in Twilio.</p>'+
'<p>This must be of the form <code>+18005550199</code>.</p>',
awPopOver: i18n._('<p>Number associated with the "Messaging Service" in Twilio.</p>'+
'<p>This must be of the form <code>+18005550199</code>.</p>'),
awRequiredWhen: {
reqExpression: "twilio_required",
init: "false"
@ -206,12 +206,12 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
to_numbers: {
label: 'Destination SMS Number',
label: i18n._('Destination SMS Number'),
type: 'textarea',
rows: 3,
awPopOver: '<p>Type an option on each line.</p>'+
'<p>For example:<br><code>+12125552368</code><br>\n<code>+19105556162</code><br>\n',
dataTitle: 'Destination SMS Number',
awPopOver: i18n._('<p>Type an option on each line.</p>'+
'<p>For example:<br><code>+12125552368</code><br>\n<code>+19105556162</code><br>\n'),
dataTitle: i18n._('Destination SMS Number'),
dataPlacement: 'right',
dataContainer: "body",
awRequiredWhen: {
@ -223,7 +223,7 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
account_sid: {
label: 'Account SID',
label: i18n._('Account SID'),
type: 'text',
awRequiredWhen: {
reqExpression: "twilio_required",
@ -234,7 +234,7 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
subdomain: {
label: 'Pagerduty subdomain',
label: i18n._('Pagerduty subdomain'),
type: 'text',
awRequiredWhen: {
reqExpression: "pagerduty_required",
@ -245,7 +245,7 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
service_key: {
label: 'API Service/Integration Key',
label: i18n._('API Service/Integration Key'),
type: 'text',
awRequiredWhen: {
reqExpression: "pagerduty_required",
@ -256,7 +256,7 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
client_name: {
label: 'Client Identifier',
label: i18n._('Client Identifier'),
type: 'text',
awRequiredWhen: {
reqExpression: "pagerduty_required",
@ -267,7 +267,7 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
message_from: {
label: 'Label to be shown with notification',
label: i18n._('Label to be shown with notification'),
type: 'text',
awRequiredWhen: {
reqExpression: "hipchat_required",
@ -290,10 +290,10 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
color: {
label: 'Notification Color',
label: i18n._('Notification Color'),
type: 'text',
awPopOver: '<p>Color can be one of <code>yellow</code>, <code>green</code>, <code>red</code>, ' +
'<code>purple</code>, <code>gray</code>, or <code>random</code>.\n',
awPopOver: i18n._('<p>Color can be one of <code>yellow</code>, <code>green</code>, <code>red</code>, ' +
'<code>purple</code>, <code>gray</code>, or <code>random</code>.\n'),
awRequiredWhen: {
reqExpression: "hipchat_required",
init: "false"
@ -303,14 +303,14 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
notify: {
label: 'Notify Channel',
label: i18n._('Notify Channel'),
type: 'checkbox',
ngShow: "notification_type.value == 'hipchat' ",
subForm: 'typeSubForm',
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
url: {
label: 'Target URL',
label: i18n._('Target URL'),
type: 'text',
awRequiredWhen: {
reqExpression: "webhook_required",
@ -321,7 +321,7 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
headers: {
label: 'HTTP Headers',
label: i18n._('HTTP Headers'),
type: 'textarea',
rows: 5,
'class': 'Form-formGroup--fullWidth',
@ -329,20 +329,20 @@ export default function() {
reqExpression: "webhook_required",
init: "false"
},
awPopOver: '<p>Specify HTTP Headers in JSON format</p>' +
awPopOver: i18n._('<p>Specify HTTP Headers in JSON format</p>' +
'<p>For example:<br><pre>\n' +
'{\n' +
' "X-Auth-Token": "828jf0",\n' +
' "X-Ansible": "Is great!"\n' +
'}\n' +
'</pre></p>',
'</pre></p>'),
dataPlacement: 'right',
ngShow: "notification_type.value == 'webhook' ",
subForm: 'typeSubForm',
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
server: {
label: 'IRC Server Address',
label: i18n._('IRC Server Address'),
type: 'text',
awRequiredWhen: {
reqExpression: "irc_required",
@ -353,7 +353,7 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
nickname: {
label: 'IRC Nick',
label: i18n._('IRC Nick'),
type: 'text',
awRequiredWhen: {
reqExpression: "irc_required",
@ -364,12 +364,12 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
targets: {
label: 'Destination Channels or Users',
label: i18n._('Destination Channels or Users'),
type: 'textarea',
rows: 3,
awPopOver: '<p>Type an option on each line. The pound symbol (#) is not required.</p>'+
'<p>For example:<br>#support or support<br>\n @username or username<br>\n',
dataTitle: 'Destination Channels',
awPopOver: i18n._('<p>Type an option on each line. The pound symbol (#) is not required.</p>'+
'<p>For example:<br>#support or support<br>\n @username or username<br>\n'),
dataTitle: i18n._('Destination Channels'),
dataPlacement: 'right',
dataContainer: "body",
awRequiredWhen: {
@ -381,27 +381,27 @@ export default function() {
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
use_ssl: {
label: 'SSL Connection',
label: i18n._('SSL Connection'),
type: 'checkbox',
ngShow: "notification_type.value == 'irc'",
subForm: 'typeSubForm',
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
},
checkbox_group: {
label: 'Options',
label: i18n._('Options'),
type: 'checkbox_group',
subForm: 'typeSubForm',
ngShow: "notification_type.value == 'email'",
fields: [{
name: 'use_tls',
label: 'Use TLS',
label: i18n._('Use TLS'),
type: 'checkbox',
ngShow: "notification_type.value == 'email' ",
labelClass: 'checkbox-options stack-inline',
ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)'
}, {
name: 'use_ssl',
label: 'Use SSL',
label: i18n._('Use SSL'),
type: 'checkbox',
ngShow: "notification_type.value == 'email'",
labelClass: 'checkbox-options stack-inline',
@ -426,4 +426,4 @@ export default function() {
}
}
};
}
}];

View File

@ -8,10 +8,10 @@
* off of the settings page
*/
export default function(){
export default ['i18n', function(i18n){
return {
name: 'notification_templates' ,
listTitle: 'Notification Templates',
listTitle: i18n._('Notification Templates'),
iterator: 'notification_template',
index: false,
hover: false,
@ -30,12 +30,12 @@ export default function(){
},
name: {
key: true,
label: 'Name',
label: i18n._('Name'),
columnClass: 'col-md-3 col-sm-9 col-xs-9',
linkTo: '/#/notification_templates/{{notification_template.id}}'
},
notification_type: {
label: 'Type',
label: i18n._('Type'),
searchType: 'select',
searchOptions: [],
excludeModal: true,
@ -47,9 +47,9 @@ export default function(){
add: {
mode: 'all', // One of: edit, select, all
ngClick: 'addNotification()',
awToolTip: 'Create a new custom inventory',
awToolTip: i18n._('Create a new custom inventory'),
actionClass: 'btn List-buttonSubmit',
buttonContent: '&#43; ADD',
buttonContent: i18n._('&#43; ADD'),
ngShow: 'canAdd'
}
},
@ -60,38 +60,38 @@ export default function(){
test: {
ngClick: "testNotification(notification_template.id)",
icon: 'fa-bell-o',
label: 'Edit',
label: i18n._('Edit'),
"class": 'btn-sm',
awToolTip: 'Test notification',
awToolTip: i18n._('Test notification'),
dataPlacement: 'top',
ngShow: 'notification_template.summary_fields.user_capabilities.edit'
},
edit: {
ngClick: "editNotification(notification_template.id)",
icon: 'fa-edit',
label: 'Edit',
label: i18n._('Edit'),
"class": 'btn-sm',
awToolTip: 'Edit notification',
awToolTip: i18n._('Edit notification'),
dataPlacement: 'top',
ngShow: 'notification_template.summary_fields.user_capabilities.edit'
},
view: {
ngClick: "editNotification(notification_template.id)",
label: 'View',
label: i18n._('View'),
"class": 'btn-sm',
awToolTip: 'View notification',
awToolTip: i18n._('View notification'),
dataPlacement: 'top',
ngShow: '!notification_template.summary_fields.user_capabilities.edit'
},
"delete": {
ngClick: "deleteNotification(notification_template.id, notification_template.name)",
icon: 'fa-trash',
label: 'Delete',
label: i18n._('Delete'),
"class": 'btn-sm',
awToolTip: 'Delete notification',
awToolTip: i18n._('Delete notification'),
dataPlacement: 'top',
ngShow: 'notification_template.summary_fields.user_capabilities.delete'
}
}
};
}
}];

View File

@ -8,13 +8,13 @@
* used in the related tabs
*/
export default function(){
export default ['i18n', function(i18n){
return {
// These tooltip fields are consumed to build disabled related tabs tooltips in the form > add view
dataPlacement: 'top',
awToolTip: 'Please save before adding notifications',
awToolTip: i18n._('Please save before adding notifications'),
name: 'notifications' ,
title: 'Notifications',
title: i18n._('Notifications'),
iterator: 'notification',
index: false,
hover: false,
@ -23,19 +23,19 @@ export default function(){
fields: {
name: {
key: true,
label: 'Name',
label: i18n._('Name'),
columnClass: 'col-md-3 col-sm-9 col-xs-9',
linkTo: '/#/notification_templates/{{notifier.id}}',
},
notification_type: {
label: 'Type',
label: i18n._('Type'),
searchType: 'select',
searchOptions: [],
excludeModal: true,
columnClass: 'col-md-4 hidden-sm hidden-xs'
},
notification_templates_success: {
label: 'Success',
label: i18n._('Success'),
flag: 'notification_templates_success',
type: "toggle",
ngClick: "toggleNotification($event, notification.id, \"notification_templates_success\")",
@ -46,7 +46,7 @@ export default function(){
nosort: true,
},
notification_templates_error: {
label: 'Failure',
label: i18n._('Failure'),
columnClass: 'NotifierList-lastColumn',
flag: 'notification_templates_error',
type: "toggle",
@ -63,12 +63,12 @@ export default function(){
label: 'Add Notification',
mode: 'all', // One of: edit, select, all
ngClick: 'addNotificationTemplate()',
awToolTip: 'Create a new notification template',
awToolTip: i18n._('Create a new notification template'),
actionClass: 'btn List-buttonSubmit',
buttonContent: '&#43; ADD NOTIFICATION TEMPLATE',
buttonContent: i18n._('&#43; ADD NOTIFICATION TEMPLATE'),
ngShow: 'current_user.is_superuser || (current_user_admin_orgs && current_user_admin_orgs.length > 0)'
}
}
};
}
}];

View File

@ -4,8 +4,8 @@
* All Rights Reserved
*************************************************/
export default [
function () {
export default ['i18n',
function (i18n) {
return{
getDetailFields: function(type) {
var obj = {};
@ -24,20 +24,20 @@ function () {
obj.room_required = false;
switch (type) {
case 'email':
obj.portLabel = ' Port';
obj.passwordLabel = ' Password';
obj.portLabel = ' ' + i18n._('Port');
obj.passwordLabel = ' ' + i18n._('Password');
obj.email_required = true;
obj.port_required = true;
obj.password_required = false;
break;
case 'slack':
obj.tokenLabel =' Token';
obj.tokenLabel =' ' + i18n._('Token');
obj.slack_required = true;
obj.token_required = true;
obj.channel_required = true;
break;
case 'hipchat':
obj.tokenLabel = ' Token';
obj.tokenLabel = ' ' + i18n._('Token');
obj.hipchat_required = true;
obj.room_required = true;
obj.token_required = true;
@ -49,13 +49,13 @@ function () {
obj.webhook_required = true;
break;
case 'pagerduty':
obj.tokenLabel = ' API Token';
obj.tokenLabel = ' ' + i18n._('API Token');
obj.pagerduty_required = true;
obj.token_required = true;
break;
case 'irc':
obj.portLabel = ' IRC Server Port';
obj.passwordLabel = ' IRC Server Password';
obj.portLabel = ' ' + i18n._('IRC Server Port');
obj.passwordLabel = ' ' + i18n._('IRC Server Password');
obj.irc_required = true;
obj.password_required = true;
obj.port_required = true;

View File

@ -3,18 +3,19 @@
<div ng-cloak id="htmlTemplate" class="Panel">
<div class="List-header">
<div class="List-title">
<div class="List-titleText">
organizations
<div class="List-titleText" translate>
ORGANIZATIONS
</div>
<span class="badge List-titleBadge">
{{ orgCount }}
</span>
</div>
<div class="List-actions">
<!-- &#43; ADD -->
<button class="btn List-buttonSubmit"
aw-tool-tip="Create a new organization"
aw-tool-tip="{{'Create a new organization'|translate}}"
ng-show="canAdd"
ng-click="addOrganization()">
ng-click="addOrganization()" translate>
+ ADD
</button>
</div>
@ -22,7 +23,7 @@
<div class="List-well">
<div class="OrgCards-search" ng-hide="organizationLoading == false && organization_active_search == false && organization_total_rows < 1">
</div>
<div class="List-noItems" ng-show="organizationLoading == false && organization_active_search == false && organization_total_rows < 1">PLEASE ADD ITEMS TO THIS LIST</div>
<div class="List-noItems" ng-show="organizationLoading == false && organization_active_search == false && organization_total_rows < 1" translate>PLEASE ADD ITEMS TO THIS LIST</div>
<div class="OrgCards" id="OrgCards">
<div class="OrgCards-card"
ng-class="{'OrgCards-card--selected': activeCard === card.id || card.isActiveCard }"

View File

@ -1,8 +1,8 @@
<ol class="BreadCrumb-list">
<li class="BreadCrumb-item" ng-repeat="step in steps | limitTo:(steps.length-1)">
<a href="{{step.ncyBreadcrumbLink}}">{{step.ncyBreadcrumbLabel}}</a>
<a href="{{step.ncyBreadcrumbLink}}">{{step.ncyBreadcrumbLabel|translate}}</a>
</li>
<li class="BreadCrumb-item" ng-repeat="step in steps | limitTo:-1" class="active">
<span>{{step.ncyBreadcrumbLabel}}</span>
<span>{{step.ncyBreadcrumbLabel|translate}}</span>
</li>
</ol>

View File

@ -7,17 +7,17 @@
<div class= "Form-tab is-selected" id="active_jobs_link"
ng-class="{'is-selected': jobsSelected }"
ng-click="toggleTab('jobs')">
Jobs
<translate>Jobs</translate>
</div>
<div id="scheduled_jobs_link" class="Form-tab"
ng-class="{'is-selected': schedulesSelected }"
ng-click="toggleTab('scheduled')">
Schedules
<translate>Schedules</translate>
</div>
</div>
<div class="Form-tabActions">
<button id="refresh_btn" ng-show="socketStatus === 'error'" aw-tool-tip="Refresh the page" data-placement="top" class="btn List-buttonDefault" ng-click="refreshJobs()" toolbar="true">
<span>REFRESH</span>
<button id="refresh_btn" ng-show="socketStatus === 'error'" aw-tool-tip="{{'Refresh the page'|translate}}" data-placement="top" class="btn List-buttonDefault" ng-click="refreshJobs()" toolbar="true">
<span translate>REFRESH</span>
</button>
</div>
</div>

View File

@ -3,13 +3,13 @@
<div class="btn-group PortalMode-filter">
<button ng-class="{'btn-primary ': activeFilter == 'user',
'btn-default' : activeFilter != 'user' }"
ng-click='filterUser()' class="btn btn-xs">My Jobs</button>
ng-click='filterUser()' class="btn btn-xs" translate>My Jobs</button>
<button ng-class="{'btn-primary' : activeFilter == 'all',
'btn-default' : activeFilter != 'all'}" ng-click='filterAll()' class="btn btn-xs btn-default">All Jobs</button>
'btn-default' : activeFilter != 'all'}" ng-click='filterAll()' class="btn btn-xs btn-default" translate>All Jobs</button>
</div>
<div class="PortalMode-refresh">
<button id="refresh_btn" aw-tool-tip="Refresh the page" data-placement="top" class="btn List-buttonDefault" ng-click="refresh()" ng-show="socketStatus == 'error'">
<span>REFRESH</span>
<span translate>REFRESH</span>
</button>
</div>
</div>

View File

@ -1,57 +1,57 @@
<section id="htmlTemplate" class="Container">
<div class="SetupMenu">
<a ui-sref="organizations" class="SetupItem">
<h4 class="SetupItem-title ">Organizations</h4>
<p class="SetupItem-description">
<h4 class="SetupItem-title " translate>Organizations</h4>
<p class="SetupItem-description" translate>
Group all of your content to manage permissions across departments in your company.
</p>
</a>
<a ui-sref="users" class="SetupItem">
<h4 class="SetupItem-title">Users</h4>
<p class="SetupItem-description">
<h4 class="SetupItem-title" translate>Users</h4>
<p class="SetupItem-description" translate>
Allow others to sign into Tower and own the content they create.
</p>
</a>
<a ui-sref="teams" class="SetupItem">
<h4 class="SetupItem-title">Teams</h4>
<p class="SetupItem-description">
<h4 class="SetupItem-title" translate>Teams</h4>
<p class="SetupItem-description" translate>
Split up your organization to associate content and control permissions for groups.
</p>
</a>
<a ui-sref="credentials" class="SetupItem">
<h4 class="SetupItem-title">Credentials</h4>
<p class="SetupItem-description">
<h4 class="SetupItem-title" translate>Credentials</h4>
<p class="SetupItem-description" translate>
Add passwords, SSH keys, etc. for Tower to use when launching jobs against machines, or when syncing inventories or projects.
</p>
</a>
<a ui-sref="managementJobsList" class="SetupItem" ng-if="user_is_superuser || user_is_system_auditor">
<h4 class="SetupItem-title">Management Jobs</h4>
<p class="SetupItem-description">
<h4 class="SetupItem-title" translate>Management Jobs</h4>
<p class="SetupItem-description" translate>
Manage the cleanup of old job history, activity streams, data marked for deletion, and system tracking info.
</p>
</a>
<a ui-sref="inventoryScripts" class="SetupItem">
<h4 class="SetupItem-title">Inventory Scripts</h4>
<p class="SetupItem-description">
<h4 class="SetupItem-title" translate>Inventory Scripts</h4>
<p class="SetupItem-description" translate>
Create and edit scripts to dynamically load hosts from any source.
</p>
</a>
<a ui-sref="notifications" class="SetupItem"
ng-if="orgAdmin || user_is_system_auditor || user_is_superuser">
<h4 class="SetupItem-title">Notifications</h4>
<p class="SetupItem-description">
<h4 class="SetupItem-title" translate>Notifications</h4>
<p class="SetupItem-description" translate>
Create templates for sending notifications with Email, HipChat, Slack, and SMS.
</p>
</a>
<a ui-sref="license" class="SetupItem">
<h4 class="SetupItem-title">View Your License</h4>
<p class="SetupItem-description">
<h4 class="SetupItem-title" translate>View Your License</h4>
<p class="SetupItem-description" translate>
View and edit your license information.
</p>
</a>
<a ui-sref="setup.about" class="SetupItem">
<h4 class="SetupItem-title">About Tower</h4>
<p class="SetupItem-description">
<h4 class="SetupItem-title" translate>About Tower</h4>
<p class="SetupItem-description" translate>
View information about this version of Ansible Tower.
</p>
</a>

View File

@ -142,10 +142,11 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
.factory('GenerateForm', ['$rootScope', '$location', '$compile', 'generateList',
'SearchWidget', 'PaginateWidget', 'Attr', 'Icon', 'Column',
'NavigationLink', 'HelpCollapse', 'DropDown', 'Empty', 'SelectIcon',
'Store', 'ActionButton', 'getSearchHtml',
'Store', 'ActionButton', 'getSearchHtml', 'i18n',
function ($rootScope, $location, $compile, GenerateList, SearchWidget,
PaginateWidget, Attr, Icon, Column, NavigationLink, HelpCollapse,
DropDown, Empty, SelectIcon, Store, ActionButton, getSearchHtml) {
DropDown, Empty, SelectIcon, Store, ActionButton, getSearchHtml,
i18n) {
return {
setForm: function (form) { this.form = form; },
@ -875,20 +876,27 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
// Add error messages
if ((options.mode === 'add' && field.addRequired) || (options.mode === 'edit' && field.editRequired) ||
field.awRequiredWhen) {
var error_message = i18n._("Please enter a value.");
html += "<div class=\"error\" id=\"" + this.form.name + "-" + fld + "-required-error\" ng-show=\"" + this.form.name + '_form.' + fld + ".$dirty && " +
this.form.name + '_form.' + fld + ".$error.required\">" + (field.requiredErrorMsg ? field.requiredErrorMsg : "Please enter a value.") + "</div>\n";
this.form.name + '_form.' + fld + ".$error.required\">" + (field.requiredErrorMsg ? field.requiredErrorMsg : error_message) + "</div>\n";
}
if (field.type === "email") {
var error_message = i18n._("Please enter a valid email address.");
html += "<div class=\"error\" id=\"" + this.form.name + "-" + fld + "-email-error\" ng-show=\"" + this.form.name + '_form.' + fld + ".$dirty && " +
this.form.name + '_form.' + fld + ".$error.email\">Please enter a valid email address.</div>\n";
this.form.name + '_form.' + fld + ".$error.email\">" +
error_message + "</div>\n";
}
if (field.awPassMatch) {
var error_message = i18n._("This value does not match the password you entered previously. Please confirm that password.");
html += "<div class=\"error\" id=\"" + this.form.name + "-" + fld + "-passmatch-error\" ng-show=\"" + this.form.name + '_form.' + fld +
".$error.awpassmatch\">This value does not match the password you entered previously. Please confirm that password.</div>\n";
".$error.awpassmatch\">" +
error_message + "</div>\n";
}
if (field.awValidUrl) {
var error_message = i18n._("Please enter a URL that begins with ssh, http or https. The URL may not contain the '@' character.");
html += "<div class=\"error\" id=\"" + this.form.name + "-" + fld + "-url-error\" ng-show=\"" + this.form.name + '_form.' + fld +
".$error.awvalidurl\">Please enter a URL that begins with ssh, http or https. The URL may not contain the '@' character. </div>\n";
".$error.awvalidurl\">" +
error_message + "</div>\n";
}
html += "<div class=\"error api-error\" id=\"" + this.form.name + "-" + fld + "-api-error\" ng-bind=\"" + fld + "_api_error\"></div>\n";
@ -901,7 +909,7 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
//fields with sensitive data that needs to be obfuscated from view
if (field.type === 'sensitive') {
field.showInputInnerHTML = "Show";
field.showInputInnerHTML = i18n._("Show");
field.inputType = "password";
html += "\t" + label();
@ -918,6 +926,7 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
$(inputId).attr("type", "password");
}
};
var tooltip = i18n._("Toggle the display of plaintext.");
html += "\<div class='input-group";
html += (horizontal) ? " " + getFieldWidth() : "";
html += "'>\n";
@ -925,7 +934,7 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
html += "<span class='input-group-btn'>\n";
html += "<button type='button' class='btn btn-default show_input_button Form-passwordButton' ";
html += buildId(field, fld + "_show_input_button", this.form);
html += "aw-tool-tip='Toggle the display of plaintext.' aw-tip-placement='top' ";
html += "aw-tool-tip='" + tooltip + "' aw-tip-placement='top' ";
html += "tabindex='-1' ";
html += "ng-click='" + fld + "_field.toggleInput(\"#" + this.form.name + "_" + fld + "\")'";
html += (field.ngDisabled) ? "ng-disabled='" + field.ngDisabled + "'" : "";
@ -997,42 +1006,59 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
// Add error messages
if ((options.mode === 'add' && field.addRequired) || (options.mode === 'edit' && field.editRequired) ||
field.awRequiredWhen) {
var error_message = i18n._("Please enter a value.");
html += "<div class='error' id='" + this.form.name + "-" + fld + "-required-error' ng-show='" + this.form.name + "_form." + fld + ".$dirty && " +
this.form.name + "_form." + fld + ".$error.required'>\n" + (field.requiredErrorMsg ? field.requiredErrorMsg : "Please enter a value.") + "\n</div>\n";
this.form.name + "_form." + fld + ".$error.required'>\n" + (field.requiredErrorMsg ? field.requiredErrorMsg : error_message) + "\n</div>\n";
}
if (field.type === "email") {
var error_message = i18n._("Please enter a valid email address.");
html += "<div class='error' id='" + this.form.name + "-" + fld + "-email-error' ng-show='" + this.form.name + "_form." + fld + ".$dirty && " +
this.form.name + "_form." + fld + ".$error.email'>\nPlease enter a valid email address.\n</div>\n";
this.form.name + "_form." + fld + ".$error.email'>\n" +
error_message + "\n</div>\n";
}
if (field.awPassMatch) {
var error_message = i18n._("This value does not match the password you entered previously. Please confirm that password.");
html += "<div class='error' id='" + this.form.name + "-" + fld + "-passmatch-error' ng-show='" + this.form.name + "_form." + fld +
".$error.awpassmatch'>\nThis value does not match the password you entered previously. Please confirm that password.\n</div>\n";
".$error.awpassmatch'>\n" +
error_message + "\n</div>\n";
}
if (field.awValidUrl) {
var error_message = i18n._("Please enter a URL that begins with ssh, http or https. The URL may not contain the '@' character.");
html += "<div class='error' id='" + this.form.name + "-" + fld + "-url-error' ng-show='" + this.form.name + "_form." + fld +
".$error.awvalidurl'>\nPlease enter a URL that begins with ssh, http or https. The URL may not contain the '@' character.\n</div>\n";
".$error.awvalidurl'>\n" +
error_message + "\n</div>\n";
}
if (field.chkPass && $AnsibleConfig) {
// password strength
if ($AnsibleConfig.password_length) {
var error_message = i18n.format(i18n._("Your password must be %d characters long."), $AnsibleConfig.password_length);
html += "<div class=\"error\" ng-show=\"" + this.form.name + '_form.' + fld +
".$error.password_length\">Your password must be " + $AnsibleConfig.password_length + " characters long.</div>\n";
".$error.password_length\">" +
error_message + "</div>\n";
}
if ($AnsibleConfig.password_hasLowercase) {
var error_message = i18n._("Your password must contain a lowercase letter.");
html += "<div class=\"error\" ng-show=\"" + this.form.name + '_form.' + fld +
".$error.hasLowercase\">Your password must contain a lowercase letter.</div>\n";
".$error.hasLowercase\">" +
error_message + "</div>\n";
}
if ($AnsibleConfig.password_hasUppercase) {
var error_message = i18n._("Your password must contain an uppercase letter.");
html += "<div class=\"error\" ng-show=\"" + this.form.name + '_form.' + fld +
".$error.hasUppercase\">Your password must contain an uppercase letter.</div>\n";
".$error.hasUppercase\">" +
error_message + "</div>\n";
}
if ($AnsibleConfig.password_hasNumber) {
var error_message = i18n._("Your password must contain a number.");
html += "<div class=\"error\" ng-show=\"" + this.form.name + '_form.' + fld +
".$error.hasNumber\">Your password must contain a number.</div>\n";
".$error.hasNumber\">" +
error_message + "</div>\n";
}
if ($AnsibleConfig.password_hasSymbol) {
var error_message = i18n.format(i18n._("Your password must contain one of the following characters: %s"), "`~!@#$%^&*()_-+=|}\]{\[;:\"\'?\/>.<,");
html += "<div class=\"error\" ng-show=\"" + this.form.name + '_form.' + fld +
".$error.hasSymbol\">Your password must contain one of the following characters: `~!@#$%^&*()_-+=|}\]{\[;:\"\'?\/>.<,</div>\n";
".$error.hasSymbol\">" +
error_message + "</div>\n";
}
}
@ -1136,7 +1162,13 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
if(!field.multiSelect && !field.disableChooseOption){
html += "<option value=\"\">";
// Add a custom default select 'value' (default text)
html += (field.defaultText) ? field.defaultText : "Choose a " + field.label.toLowerCase();
if (field.defaultText) {
html += field.defaultText;
} else {
// i18n is used with src/forms/Projects.js
html += i18n.format(i18n._("Choose a %s"),
field.label.toLowerCase());
}
html += "</option>\n";
}
@ -1488,10 +1520,14 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
html += "<div class=\"Form-title\">";
html += (options.mode === 'edit') ? this.form.editTitle : this.form.addTitle;
if(this.form.name === "user"){
var user_str = i18n._("Admin");
html+= "<span class=\"Form-title--is_superuser\" "+
"ng-show='is_superuser'>Admin</span>";
"ng-show='is_superuser'>" +
user_str + "</span>";
user_str = i18n._("Auditor");
html+= "<span class=\"Form-title--is_system_auditor\" "+
"ng-show='is_system_auditor'>Auditor</span>";
"ng-show='is_system_auditor'>" +
user_str + "</span>";
html+= "<span class=\"Form-title--is_ldap_user\" "+
"ng-show='ldap_user'>LDAP</span>";
html+= "<span class=\"Form-title--is_external_account\" "+
@ -1521,13 +1557,16 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
if (!_.isEmpty(this.form.related)) {
var collection;
// i18n is used with src/forms/Projects.js
var details = i18n._("Details");
html += "<div class=\"Form-tabHolder\">";
if(this.mode === "edit"){
html += "<div id=\"" + this.form.name + "_tab\""+
"class=\"Form-tab\" "+
"ng-click=\"toggleFormTabs($event)\"" +
"ng-class=\"{'is-selected': " + this.form.name + "Selected }\">Details</div>";
"ng-class=\"{'is-selected': " + this.form.name + "Selected }\">" +
details + "</div>";
for (itm in this.form.related) {
collection = this.form.related[itm];
@ -1548,7 +1587,8 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
}
else if(this.mode === "add"){
html += "<div id=\"" + this.form.name + "_tab\""+
"class=\"Form-tab is-selected\">Details</div>";
"class=\"Form-tab is-selected\">" +
details + "</div>";
for (itm in this.form.related) {
collection = this.form.related[itm];
@ -1693,31 +1733,31 @@ angular.module('FormGenerator', [GeneratorHelpers.name, 'Utilities', listGenerat
// Set default color and label for Save and Reset
if (btn === 'save') {
button.label = 'Save';
button.label = i18n._('Save');
button['class'] = 'Form-saveButton';
}
if (btn === 'cancel') {
button.label = 'Cancel';
button.label = i18n._('Cancel');
button['class'] = 'Form-cancelButton';
}
if (btn === 'close') {
button.label = 'Close';
button.label = i18n._('Close');
button['class'] = 'Form-cancelButton';
}
if (btn === 'launch') {
button.label = 'Launch';
button.label = i18n._('Launch');
button['class'] = 'Form-launchButton';
}
if (btn === 'add_survey') {
button.label = 'Add Survey';
button.label = i18n._('Add Survey');
button['class'] = 'Form-surveyButton';
}
if (btn === 'edit_survey') {
button.label = 'Edit Survey';
button.label = i18n._('Edit Survey');
button['class'] = 'Form-surveyButton';
}
if (btn === 'view_survey') {
button.label = 'View Survey';
button.label = i18n._('View Survey');
button['class'] = 'Form-surveyButton';
}

View File

@ -100,8 +100,9 @@ import {templateUrl} from '../../shared/template-url/template-url.factory';
export default ['$location', '$compile', '$rootScope', 'SearchWidget', 'PaginateWidget', 'Attr', 'Icon', 'getSearchHtml',
'Column', 'DropDown', 'NavigationLink', 'SelectIcon', 'ActionButton',
'i18n',
function ($location, $compile, $rootScope, SearchWidget, PaginateWidget, Attr, Icon, getSearchHtml, Column, DropDown, NavigationLink,
SelectIcon, ActionButton) {
SelectIcon, ActionButton, i18n) {
return {
setList: function (list) {
@ -310,7 +311,7 @@ export default ['$location', '$compile', '$rootScope', 'SearchWidget', 'Paginate
if (list.listTitle) {
html += "<div class=\"List-titleText\">" + list.listTitle + "</div>";
html += "<div class=\"List-titleText\"><translate>" + list.listTitle + "</translate></div>";
// We want to show the list title badge by default and only hide it when the list config specifically passes a false flag
list.listTitleBadge = (typeof list.listTitleBadge === 'boolean' && list.listTitleBadge === false) ? false : true;
if(list.listTitleBadge) {
@ -411,7 +412,7 @@ export default ['$location', '$compile', '$rootScope', 'SearchWidget', 'Paginate
// Show the "no items" box when loading is done and the user isn't actively searching and there are no results
html += "<div class=\"List-noItems\" ng-show=\"" + list.iterator + "Loading == false && " + list.iterator + "_active_search == false && " + list.iterator + "_total_rows < 1\">";
html += (list.emptyListText) ? list.emptyListText : "PLEASE ADD ITEMS TO THIS LIST";
html += (list.emptyListText) ? list.emptyListText : i18n._("PLEASE ADD ITEMS TO THIS LIST");
html += "</div>";
// Add a title and optionally a close button (used on Inventory->Groups)
@ -580,7 +581,9 @@ export default ['$location', '$compile', '$rootScope', 'SearchWidget', 'Paginate
// Message for loading
innerTable += "<tr class=\"loading-info\" ng-show=\"" + list.iterator + "Loading == true\">\n";
innerTable += "<td colspan=\"" + cnt + "\" class=\"List-tableCell\"><div class=\"loading-info\">Loading...</div></td>\n";
var loading = i18n._("Loading...");
innerTable += "<td colspan=\"" + cnt + "\" class=\"List-tableCell\"><div class=\"loading-info\">" +
loading + "</div></td>\n";
innerTable += "</tr>\n";
// End List
@ -684,7 +687,7 @@ export default ['$location', '$compile', '$rootScope', 'SearchWidget', 'Paginate
html += "<th class=\"List-tableHeader List-tableHeader--actions actions-column";
html += (list.fieldActions && list.fieldActions.columnClass) ? " " + list.fieldActions.columnClass : "";
html += "\">";
html += (list.fieldActions.label === undefined || list.fieldActions.label) ? "Actions" : "";
html += (list.fieldActions.label === undefined || list.fieldActions.label) ? i18n._("Actions") : "";
html += "</th>\n";
}
html += "</tr>\n";

View File

@ -5,8 +5,8 @@
*************************************************/
import ReconnectingWebSocket from 'reconnectingwebsocket';
export default
['$rootScope', '$location', '$log','$state', '$q',
function ($rootScope, $location, $log, $state, $q) {
['$rootScope', '$location', '$log','$state', '$q', 'i18n',
function ($rootScope, $location, $log, $state, $q, i18n) {
var needsResubscribing = false,
socketPromise = $q.defer();
return {
@ -169,15 +169,15 @@ export default
if(self.socket){
if (self.socket.readyState === 0 ) {
$rootScope.socketStatus = 'connecting';
$rootScope.socketTip = "Live events: attempting to connect to the Tower server.";
$rootScope.socketTip = i18n._("Live events: attempting to connect to the Tower server.");
}
else if (self.socket.readyState === 1){
$rootScope.socketStatus = 'ok';
$rootScope.socketTip = "Live events: connected. Pages containing job status information will automatically update in real-time.";
$rootScope.socketTip = i18n._("Live events: connected. Pages containing job status information will automatically update in real-time.");
}
else if (self.socket.readyState === 2 || self.socket.readyState === 3 ){
$rootScope.socketStatus = 'error';
$rootScope.socketTip = "Live events: error connecting to the Tower server.";
$rootScope.socketTip = i18n._("Live events: error connecting to the Tower server.");
}
return;
}

View File

@ -1,9 +1,9 @@
module.exports = {
dev: {
tasks: ['copy:vendor', 'copy:assets', 'copy:partials', 'copy:config', 'less:dev'],
tasks: ['copy:vendor', 'copy:assets', 'copy:partials', 'copy:languages', 'copy:config', 'less:dev'],
},
prod: {
tasks: ['newer:copy:vendor', 'newer:copy:assets', 'newer:copy:partials', 'newer:copy:config', 'newer:less:prod']
tasks: ['newer:copy:vendor', 'newer:copy:assets', 'newer:copy:partials', 'newer:copy:languages', 'newer:copy:config', 'newer:less:prod']
},
watch: {
tasks: ['watch:css', 'watch:partials', 'watch:assets', ['webpack:dev', 'watch:config']],

View File

@ -43,6 +43,14 @@ module.exports = {
dest: 'static/partials/'
}]
},
languages: {
files: [{
cwd: 'client/',
expand: true,
src: 'languages/*.json',
dest: 'static/'
}]
},
config: {
files: { 'static/config.js': ['client/src/config.js'] }
}

View File

@ -0,0 +1,15 @@
module.exports = {
all: {
options: {
format: 'json'
},
files: [ {
expand: true,
dot: true,
dest: 'client/languages',
cwd: 'po',
ext: '.json',
src: ['*.po']
} ]
}
};

View File

@ -0,0 +1,11 @@
module.exports = {
all: {
options: {
markerNames: ['_', 'N_']
},
files: {
'po/ansible-tower.pot': ['client/src/**/*.js',
'client/src/**/*.html']
}
},
};

View File

@ -18,6 +18,8 @@
"scripts": {
"build-docker-machine": "ip=$(docker-machine ip $DOCKER_MACHINE_NAME); npm set ansible-tower:django_host ${ip}; grunt dev",
"build-docker-cid": "ip=`docker inspect --format '{{ .NetworkSettings.IPAddress }}' $DOCkER_CID` | npm set config ansible-tower:django_host ${ip}; grunt dev",
"pot": "grunt nggettext_extract",
"languages": "grunt nggettext_compile",
"build-release": "grunt release",
"pretest": "grunt clean:coverage",
"test": "karma start karma.conf.js",
@ -34,6 +36,7 @@
"browser-sync": "^2.14.0",
"expose-loader": "^0.7.1",
"grunt": "^1.0.1",
"grunt-angular-gettext": "^2.2.3",
"grunt-browser-sync": "^2.2.0",
"grunt-cli": "^1.2.0",
"grunt-concurrent": "^2.3.0",
@ -75,6 +78,7 @@
"angular-codemirror": "chouseknecht/angular-codemirror#1.0.4",
"angular-cookies": "^1.4.3",
"angular-drag-and-drop-lists": "leigh-johnson/angular-drag-and-drop-lists#1.4.0",
"angular-gettext": "^2.3.5",
"angular-md5": "^0.1.8",
"angular-moment": "^0.10.1",
"angular-resource": "^1.4.3",

2041
awx/ui/po/ansible-tower.pot Normal file

File diff suppressed because it is too large Load Diff

View File

@ -7,6 +7,7 @@ var vendorPkgs = [
'angular-codemirror',
'angular-cookies',
'angular-drag-and-drop-lists',
'angular-gettext',
'angular-md5',
'angular-moment',
'angular-sanitize',