From c01eccca7ef0b0b6f337d1c86e67c3ba7c080d99 Mon Sep 17 00:00:00 2001 From: Bill Nottingham Date: Tue, 24 Jan 2017 01:50:34 -0500 Subject: [PATCH 001/178] Remove most SELinux policy. Add file contexts to policy instead of using semanage. --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 28668066f9..3928505dfe 100644 --- a/Makefile +++ b/Makefile @@ -690,6 +690,7 @@ rpm-build: rpm-build/$(SDIST_TAR_FILE): rpm-build dist/$(SDIST_TAR_FILE) cp packaging/rpm/$(NAME).spec rpm-build/ cp packaging/rpm/tower.te rpm-build/ + cp packaging/rpm/tower.fc rpm-build/ cp packaging/rpm/$(NAME).sysconfig rpm-build/ cp packaging/remove_tower_source.py rpm-build/ cp packaging/bytecompile.sh rpm-build/ From 813cbdbbc2dcaba25308a2e69eebdf6b847e30cd Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Fri, 27 Jan 2017 11:48:22 -0500 Subject: [PATCH 002/178] include null prompted situation in JT validator --- awx/api/serializers.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 2845328bad..4ff9e40443 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -1950,16 +1950,25 @@ class JobTemplateSerializer(JobTemplateMixin, UnifiedJobTemplateSerializer, JobO return res def validate(self, attrs): - survey_enabled = attrs.get('survey_enabled', self.instance and self.instance.survey_enabled or False) - job_type = attrs.get('job_type', self.instance and self.instance.job_type or None) - inventory = attrs.get('inventory', self.instance and self.instance.inventory or None) - project = attrs.get('project', self.instance and self.instance.project or None) + def get_field_from_model_or_attrs(fd): + return attrs.get(fd, self.instance and getattr(self.instance, fd) or None) + survey_enabled = get_field_from_model_or_attrs('survey_enabled') + job_type = get_field_from_model_or_attrs('job_type') + inventory = get_field_from_model_or_attrs('inventory') + credential = get_field_from_model_or_attrs('credential') + project = get_field_from_model_or_attrs('project') + + prompting_error_message = _("Must either set a default value or ask to prompt on launch.") if job_type == "scan": if inventory is None or attrs.get('ask_inventory_on_launch', False): raise serializers.ValidationError({'inventory': _('Scan jobs must be assigned a fixed inventory.')}) elif project is None: raise serializers.ValidationError({'project': _("Job types 'run' and 'check' must have assigned a project.")}) + elif credential is None and not get_field_from_model_or_attrs('ask_credential_on_launch'): + raise serializers.ValidationError({'credential': prompting_error_message}) + elif inventory is None and not get_field_from_model_or_attrs('ask_inventory_on_launch'): + raise serializers.ValidationError({'inventory': prompting_error_message}) if survey_enabled and job_type == PERM_INVENTORY_SCAN: raise serializers.ValidationError({'survey_enabled': _('Survey Enabled cannot be used with scan jobs.')}) From 08832428cc1ffc3a8a67ee731d594d5d460dfabd Mon Sep 17 00:00:00 2001 From: Chris Meyers Date: Fri, 27 Jan 2017 13:53:57 -0500 Subject: [PATCH 003/178] don't be looping if there ain't nothing to loop on --- awx/ui/client/src/controllers/Credentials.js | 37 +++++++++++--------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/awx/ui/client/src/controllers/Credentials.js b/awx/ui/client/src/controllers/Credentials.js index 8fc0d4e55f..7d75416a05 100644 --- a/awx/ui/client/src/controllers/Credentials.js +++ b/awx/ui/client/src/controllers/Credentials.js @@ -38,31 +38,34 @@ export function CredentialsList($scope, $rootScope, $location, $log, $scope.$on(`${list.iterator}_options`, function(event, data){ $scope.options = data.data.actions.GET; + console.log($scope.options); optionsRequestDataProcessing(); }); $scope.$watchCollection(`${$scope.list.name}`, function() { - optionsRequestDataProcessing(); - } - ); + optionsRequestDataProcessing(); + }); + // iterate over the list and add fields like type label, after the // OPTIONS request returns, or the list is sorted/paginated/searched function optionsRequestDataProcessing(){ - $scope[list.name].forEach(function(item, item_idx) { - var itm = $scope[list.name][item_idx]; + if ($scope[list.name] !== undefined) { + $scope[list.name].forEach(function(item, item_idx) { + var itm = $scope[list.name][item_idx]; - // Set the item type label - if (list.fields.kind && $scope.options && - $scope.options.hasOwnProperty('kind')) { - $scope.options.kind.choices.every(function(choice) { - if (choice[0] === item.kind) { - itm.kind_label = choice[1]; - return false; - } - return true; - }); - } - }); + // Set the item type label + if (list.fields.kind && $scope.options && + $scope.options.hasOwnProperty('kind')) { + $scope.options.kind.choices.every(function(choice) { + if (choice[0] === item.kind) { + itm.kind_label = choice[1]; + return false; + } + return true; + }); + } + }); + } } $scope.addCredential = function() { From 0dbd9e48392cf0250288a9435a75d6ceec8bcba6 Mon Sep 17 00:00:00 2001 From: AlanCoding Date: Fri, 27 Jan 2017 13:46:21 -0500 Subject: [PATCH 004/178] get WFJT nodes in activity stream --- awx/api/serializers.py | 8 ++++++-- awx/api/views.py | 2 +- awx/main/access.py | 2 +- awx/main/models/__init__.py | 1 + 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index 2845328bad..5a3691efe2 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -2958,6 +2958,10 @@ class ActivityStreamSerializer(BaseSerializer): changes = serializers.SerializerMethodField() object_association = serializers.SerializerMethodField() + # Needed related fields that are not in the default summary fields + extra_listing = [ + ('workflow_job_template_node', ('id', 'unified_job_template_id')) + ] class Meta: model = ActivityStream @@ -2999,7 +3003,7 @@ class ActivityStreamSerializer(BaseSerializer): rel = {} if obj.actor is not None: rel['actor'] = reverse('api:user_detail', args=(obj.actor.pk,)) - for fk, __ in SUMMARIZABLE_FK_FIELDS.items(): + for fk, __ in SUMMARIZABLE_FK_FIELDS.items() + self.extra_listing: if not hasattr(obj, fk): continue allm2m = getattr(obj, fk).all() @@ -3021,7 +3025,7 @@ class ActivityStreamSerializer(BaseSerializer): def get_summary_fields(self, obj): summary_fields = OrderedDict() - for fk, related_fields in SUMMARIZABLE_FK_FIELDS.items(): + for fk, related_fields in SUMMARIZABLE_FK_FIELDS.items() + self.extra_listing: try: if not hasattr(obj, fk): continue diff --git a/awx/api/views.py b/awx/api/views.py index bf59089cdf..15e66baed5 100644 --- a/awx/api/views.py +++ b/awx/api/views.py @@ -3107,7 +3107,7 @@ class WorkflowJobTemplateActivityStreamList(WorkflowsEnforcementMixin, ActivityS self.check_parent_access(parent) qs = self.request.user.get_queryset(self.model) return qs.filter(Q(workflow_job_template=parent) | - Q(workflow_job_template_node__workflow_job_template=parent)) + Q(workflow_job_template_node__workflow_job_template=parent)).distinct() class WorkflowJobList(WorkflowsEnforcementMixin, ListCreateAPIView): diff --git a/awx/main/access.py b/awx/main/access.py index 196d58b6c6..2c0fdd677c 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -2101,7 +2101,7 @@ class ActivityStreamAccess(BaseAccess): 'job_template', 'job', 'ad_hoc_command', 'notification_template', 'notification', 'label', 'role', 'actor', 'schedule', 'custom_inventory_script', 'unified_job_template', - 'workflow_job_template', 'workflow_job') + 'workflow_job_template', 'workflow_job', 'workflow_job_template_node') if self.user.is_superuser or self.user.is_system_auditor: return qs.all() diff --git a/awx/main/models/__init__.py b/awx/main/models/__init__.py index 3f7e309940..d7b65d8107 100644 --- a/awx/main/models/__init__.py +++ b/awx/main/models/__init__.py @@ -126,4 +126,5 @@ activity_stream_registrar.connect(Notification) activity_stream_registrar.connect(Label) activity_stream_registrar.connect(User) activity_stream_registrar.connect(WorkflowJobTemplate) +activity_stream_registrar.connect(WorkflowJobTemplateNode) activity_stream_registrar.connect(WorkflowJob) From e99d974ea86c5f2ac9fc29df98f2617c4571a831 Mon Sep 17 00:00:00 2001 From: Bill Nottingham Date: Fri, 27 Jan 2017 16:09:29 -0500 Subject: [PATCH 005/178] Fix some apparent syntax errors in po file. --- awx/locale/fr/LC_MESSAGES/django.po | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/awx/locale/fr/LC_MESSAGES/django.po b/awx/locale/fr/LC_MESSAGES/django.po index 720fe8b149..2e429294cf 100644 --- a/awx/locale/fr/LC_MESSAGES/django.po +++ b/awx/locale/fr/LC_MESSAGES/django.po @@ -1981,7 +1981,7 @@ msgid "" "Cloud-based inventory sources (such as %s) require credentials for the " "matching cloud service." msgstr "" -"Les sources d'inventaire cloud (telles que% s) requièrent des informations " +"Les sources d'inventaire cloud (telles que %s) requièrent des informations " "d'identification pour le service cloud correspondant." #: main/models/inventory.py:980 @@ -2522,7 +2522,7 @@ msgstr "status_str doit être une réussite ou un échec" #: main/utils/common.py:89 #, python-format msgid "Unable to convert \"%s\" to boolean" -msgstr "Impossible de convertir \"% s\" en booléen" +msgstr "Impossible de convertir \"%s\" en booléen" #: main/utils/common.py:243 #, python-format @@ -3808,8 +3808,8 @@ msgstr "Votre compte est inactif" #, python-format msgid "DN must include \"%%(user)s\" placeholder for username: %s" msgstr "" -"Le ND doit inclure l'espace réservé \"%% (user)s\" pour le nom d'utilisateur" -" : % s" +"Le ND doit inclure l'espace réservé \"%%(user)s\" pour le nom d'utilisateur" +" : %s" #: sso/validators.py:26 #, python-format From 50970f2c3130abaeba19d2721c09ba472833bb0d Mon Sep 17 00:00:00 2001 From: Chris Meyers Date: Fri, 27 Jan 2017 16:31:30 -0500 Subject: [PATCH 006/178] disable playbook when permission denied to list --- awx/ui/client/src/forms/JobTemplates.js | 2 +- .../edit-job-template/job-template-edit.controller.js | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/awx/ui/client/src/forms/JobTemplates.js b/awx/ui/client/src/forms/JobTemplates.js index 28462577ae..ce0af8ff83 100644 --- a/awx/ui/client/src/forms/JobTemplates.js +++ b/awx/ui/client/src/forms/JobTemplates.js @@ -121,7 +121,7 @@ export default 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 || canAddJobTemplate)", + ngDisabled: "(job_type.value === 'scan' && project_name === 'Default') || !(job_template_obj.summary_fields.user_capabilities.edit || canAddJobTemplate) || disablePlaybookBecausePermissionDenied", id: 'playbook-select', awRequiredWhen: { reqExpression: "playbookrequired", diff --git a/awx/ui/client/src/templates/job_templates/edit-job-template/job-template-edit.controller.js b/awx/ui/client/src/templates/job_templates/edit-job-template/job-template-edit.controller.js index 16904cdaf1..8da8a4c034 100644 --- a/awx/ui/client/src/templates/job_templates/edit-job-template/job-template-edit.controller.js +++ b/awx/ui/client/src/templates/job_templates/edit-job-template/job-template-edit.controller.js @@ -98,6 +98,7 @@ export default Rest.setUrl(url); Rest.get() .success(function (data) { + $scope.disablePlaybookBecausePermissionDenied = false; $scope.playbook_options = []; var playbookNotFound = true; for (var i = 0; i < data.length; i++) { @@ -118,6 +119,7 @@ export default .error(function (ret,status_code) { if (status_code === 403) { /* user doesn't have access to see the project, no big deal. */ + $scope.disablePlaybookBecausePermissionDenied = true; } else { Alert('Missing Playbooks', 'Unable to retrieve the list of playbooks for this project. Choose a different ' + ' project or make the playbooks available on the file system.', 'alert-info'); From 66426aa7dddd94cfcf8a5639f28a9ea9b52b0dc6 Mon Sep 17 00:00:00 2001 From: Matthew Jones Date: Fri, 27 Jan 2017 16:51:03 -0500 Subject: [PATCH 007/178] Store project update and inventory update stdout in the database So things work across the cluster --- awx/main/tasks.py | 18 ++++++++++++++++++ awx/main/utils/common.py | 5 ++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/awx/main/tasks.py b/awx/main/tasks.py index 216a4f4534..4da3da0f59 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -1314,6 +1314,15 @@ class RunProjectUpdate(BaseTask): ''' return kwargs.get('private_data_files', {}).get('scm_credential', '') + def get_stdout_handle(self, instance): + stdout_handle = super(RunProjectUpdate, self).get_stdout_handle(instance) + + def raw_callback(data): + instance_actual = ProjectUpdate.objects.get(pk=instance.pk) + instance_actual.result_stdout_text += data + instance_actual.save() + return OutputEventFilter(stdout_handle, raw_callback=raw_callback) + def post_run_hook(self, instance, status, **kwargs): if instance.job_type == 'check' and status not in ('failed', 'canceled',): p = instance.project @@ -1666,6 +1675,15 @@ class RunInventoryUpdate(BaseTask): args.append('--traceback') return args + def get_stdout_handle(self, instance): + stdout_handle = super(RunInventoryUpdate, self).get_stdout_handle(instance) + + def raw_callback(data): + instance_actual = InventoryUpdate.objects.get(pk=instance.pk) + instance_actual.result_stdout_text += data + instance_actual.save() + return OutputEventFilter(stdout_handle, raw_callback=raw_callback) + def build_cwd(self, inventory_update, **kwargs): return self.get_path_to('..', 'plugins', 'inventory') diff --git a/awx/main/utils/common.py b/awx/main/utils/common.py index bed0588287..d571408122 100644 --- a/awx/main/utils/common.py +++ b/awx/main/utils/common.py @@ -766,9 +766,10 @@ class OutputEventFilter(object): EVENT_DATA_RE = re.compile(r'\x1b\[K((?:[A-Za-z0-9+/=]+\x1b\[\d+D)+)\x1b\[K') - def __init__(self, fileobj=None, event_callback=None): + def __init__(self, fileobj=None, event_callback=None, raw_callback=None): self._fileobj = fileobj self._event_callback = event_callback + self._raw_callback = raw_callback self._counter = 1 self._start_line = 0 self._buffer = '' @@ -781,6 +782,8 @@ class OutputEventFilter(object): if self._fileobj: self._fileobj.write(data) self._buffer += data + if self._raw_callback: + self._raw_callback(data) while True: match = self.EVENT_DATA_RE.search(self._buffer) if not match: From 352df064f948cd58652138e51413d1d5ae3e71cb Mon Sep 17 00:00:00 2001 From: John Mitchell Date: Fri, 27 Jan 2017 16:57:34 -0500 Subject: [PATCH 008/178] fix stdout order --- .../src/job-results/job-results.controller.js | 103 ++++-------------- .../src/job-results/parse-stdout.service.js | 6 + 2 files changed, 28 insertions(+), 81 deletions(-) diff --git a/awx/ui/client/src/job-results/job-results.controller.js b/awx/ui/client/src/job-results/job-results.controller.js index 91226e8b59..84e1dd6820 100644 --- a/awx/ui/client/src/job-results/job-results.controller.js +++ b/awx/ui/client/src/job-results/job-results.controller.js @@ -297,94 +297,35 @@ function(jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTy } if(change === 'stdout'){ - // put stdout elements in stdout container + if (!$scope.events[mungedEvent.counter]) { + // line hasn't been put in the pane yet + // create new child scope + $scope.events[mungedEvent.counter] = $scope.$new(); + $scope.events[mungedEvent.counter] + .event = mungedEvent; - var appendToBottom = function(mungedEvent){ - // if we get here then the event type was either a - // header line, recap line, or one of the additional - // event types, so we append it to the bottom. - // These are the event types for captured - // stdout not directly related to playbook or runner - // events: - // (0, 'debug', _('Debug'), False), - // (0, 'verbose', _('Verbose'), False), - // (0, 'deprecated', _('Deprecated'), False), - // (0, 'warning', _('Warning'), False), - // (0, 'system_warning', _('System Warning'), False), - // (0, 'error', _('Error'), True), - angular + // let's see if we have a specific place to put it in + // the pane + let $prevElem = $(`.next_is_${mungedEvent.start_line}`); + if ($prevElem.length) { + // if so, put it there + $(`.next_is_${mungedEvent.start_line}`) + .after($compile(mungedEvent + .stdout)($scope.events[mungedEvent + .counter])); + } else { + // if not, put it at the bottom + angular .element(".JobResultsStdOut-stdoutContainer") .append($compile(mungedEvent .stdout)($scope.events[mungedEvent .counter])); - }; - - - // this scopes the event to that particular - // block of stdout. - // If you need to see the event a particular - // stdout block is from, you can: - // angular.element($0).scope().event - $scope.events[mungedEvent.counter] = $scope.$new(); - $scope.events[mungedEvent.counter] - .event = mungedEvent; - - if (mungedEvent.stdout.indexOf("not_skeleton") > -1) { - // put non-duplicate lines into the standard - // out pane where they should go (within the - // right header section, before the next line - // as indicated by start_line) - window.$ = $; - var putIn; - var classList = $("div", - "
"+mungedEvent.stdout+"
") - .attr("class").split(" "); - if (classList - .filter(v => v.indexOf("task_") > -1) - .length) { - putIn = classList - .filter(v => v.indexOf("task_") > -1)[0]; - } else if(classList - .filter(v => v.indexOf("play_") > -1) - .length) { - putIn = classList - .filter(v => v.indexOf("play_") > -1)[0]; - } else { - appendToBottom(mungedEvent); } - var putAfter; - var isDup = false; - $(".header_" + putIn + ",." + putIn) - .each((i, v) => { - if (angular.element(v).scope() - .event.start_line < mungedEvent - .start_line) { - putAfter = v; - } else if (angular.element(v).scope() - .event.start_line === mungedEvent - .start_line) { - isDup = true; - return false; - } else if (angular.element(v).scope() - .event.start_line > mungedEvent - .start_line) { - return false; - } - }); - - if (!isDup) { - $(putAfter).after($compile(mungedEvent - .stdout)($scope.events[mungedEvent - .counter])); - } - - - classList = null; - putIn = null; - } else { - appendToBottom(mungedEvent); + // delete ref to the elem because it might leak scope + // if you don't + $prevElem = null; } // move the followAnchor to the bottom of the @@ -453,7 +394,7 @@ function(jobData, jobDataOptions, jobLabels, jobFinished, count, $scope, ParseTy }; $scope.stdoutContainerAvailable.promise.then(() => { - getSkeleton(jobData.related.job_events + "?order_by=id&or__event__in=playbook_on_start,playbook_on_play_start,playbook_on_task_start,playbook_on_stats"); + getSkeleton(jobData.related.job_events + "?order_by=start_line&or__event__in=playbook_on_start,playbook_on_play_start,playbook_on_task_start,playbook_on_stats"); }); var getEvents; diff --git a/awx/ui/client/src/job-results/parse-stdout.service.js b/awx/ui/client/src/job-results/parse-stdout.service.js index 20cb46f3c0..8630c0fa07 100644 --- a/awx/ui/client/src/job-results/parse-stdout.service.js +++ b/awx/ui/client/src/job-results/parse-stdout.service.js @@ -78,6 +78,12 @@ export default ['$log', 'moment', function($log, moment){ // .JobResultsStdOut-aLineOfStdOut element getLineClasses: function(event, line, lineNum) { var string = ""; + + if (lineNum === event.end_line) { + // used to tell you where to put stuff in the pane + string += ` next_is_${event.end_line + 1}`; + } + if (event.event_name === "playbook_on_play_start") { // play header classes string += " header_play"; From a83b518399494bd90b85c617070542ed6ab7b38c Mon Sep 17 00:00:00 2001 From: John Mitchell Date: Fri, 27 Jan 2017 17:00:34 -0500 Subject: [PATCH 009/178] workaround for first line to be put in the correct place --- .../job-results-stdout/job-results-stdout.partial.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.partial.html b/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.partial.html index b0b3d85f10..c283d578f7 100644 --- a/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.partial.html +++ b/awx/ui/client/src/job-results/job-results-stdout/job-results-stdout.partial.html @@ -44,6 +44,9 @@
Too much previous output to display. Showing running standard output.
+ +
Date: Sat, 28 Jan 2017 10:28:26 -0500 Subject: [PATCH 010/178] Marking strings for translation. This covers a lot of breadcrumbs as well as login, activity stream and project list --- awx/ui/client/src/about/about.route.js | 3 +- .../activity-stream/activitystream.route.js | 4 ++- .../src/bread-crumb/bread-crumb.directive.js | 6 ++-- .../src/configuration/configuration.route.js | 3 +- awx/ui/client/src/dashboard/hosts/main.js | 3 +- awx/ui/client/src/helpers/ActivityStream.js | 28 ++++++++-------- awx/ui/client/src/inventories/main.js | 7 ++-- .../inventories/manage/adhoc/adhoc.route.js | 3 +- .../manage/copy-move/copy-move.route.js | 5 +-- awx/ui/client/src/inventory-scripts/main.js | 3 +- awx/ui/client/src/license/license.route.js | 3 +- awx/ui/client/src/lists/Schedules.js | 33 ++++++++++--------- awx/ui/client/src/lists/Streams.js | 23 ++++++------- .../login/loginModal/loginModal.partial.html | 2 +- .../notifications/notification.route.js | 6 ++-- .../src/management-jobs/scheduler/main.js | 9 ++--- .../scheduler/schedulerForm.partial.html | 20 +++++------ awx/ui/client/src/notifications/main.js | 5 +-- .../linkout/addUsers/addUsers.partial.html | 2 +- .../linkout/organizations-linkout.route.js | 13 ++++---- awx/ui/client/src/organizations/main.js | 3 +- .../src/portal-mode/portal-mode.route.js | 3 +- awx/ui/client/src/scheduler/main.js | 21 ++++++------ awx/ui/client/src/setup-menu/setup.route.js | 3 +- .../src/shared/paginate/paginate.partial.html | 6 ++-- .../smart-search/smart-search.controller.js | 14 ++++++-- .../smart-search/smart-search.partial.html | 14 ++++---- .../src/shared/stateDefinitions.factory.js | 6 ++-- .../system-tracking/system-tracking.route.js | 3 +- .../templates/list/templates-list.route.js | 4 ++- 30 files changed, 147 insertions(+), 111 deletions(-) diff --git a/awx/ui/client/src/about/about.route.js b/awx/ui/client/src/about/about.route.js index 475cf1aea0..639c5ee975 100644 --- a/awx/ui/client/src/about/about.route.js +++ b/awx/ui/client/src/about/about.route.js @@ -1,12 +1,13 @@ import {templateUrl} from '../shared/template-url/template-url.factory'; import controller from './about.controller'; +import { N_ } from '../i18n'; export default { name: 'setup.about', route: '/about', controller: controller, ncyBreadcrumb: { - label: "ABOUT" + label: N_("ABOUT") }, onExit: function(){ // hacky way to handle user browsing away via URL bar diff --git a/awx/ui/client/src/activity-stream/activitystream.route.js b/awx/ui/client/src/activity-stream/activitystream.route.js index 73877d5f1b..5b99ce55bd 100644 --- a/awx/ui/client/src/activity-stream/activitystream.route.js +++ b/awx/ui/client/src/activity-stream/activitystream.route.js @@ -4,6 +4,8 @@ * All Rights Reserved *************************************************/ + import { N_ } from '../i18n'; + export default { name: 'activityStream', route: '/activity_stream?target&id', @@ -22,7 +24,7 @@ export default { } }, ncyBreadcrumb: { - label: "ACTIVITY STREAM" + label: N_("ACTIVITY STREAM") }, onExit: function() { $('#stream-detail-modal').modal('hide'); diff --git a/awx/ui/client/src/bread-crumb/bread-crumb.directive.js b/awx/ui/client/src/bread-crumb/bread-crumb.directive.js index 3c64a0b701..979ebd69d5 100644 --- a/awx/ui/client/src/bread-crumb/bread-crumb.directive.js +++ b/awx/ui/client/src/bread-crumb/bread-crumb.directive.js @@ -1,6 +1,6 @@ export default - ['templateUrl', '$state', 'FeaturesService', 'ProcessErrors','$rootScope', 'Store', 'Empty', '$window', 'BreadCrumbService', - function(templateUrl, $state, FeaturesService, ProcessErrors, $rootScope, Store, Empty, $window, BreadCrumbService) { + ['templateUrl', '$state', 'FeaturesService', 'ProcessErrors','$rootScope', 'Store', 'Empty', '$window', 'BreadCrumbService', 'i18n', + function(templateUrl, $state, FeaturesService, ProcessErrors, $rootScope, Store, Empty, $window, BreadCrumbService, i18n) { return { restrict: 'E', templateUrl: templateUrl('bread-crumb/bread-crumb'), @@ -103,7 +103,7 @@ export default if(features){ scope.loadingLicense = false; scope.activityStreamActive = (toState.name === 'activityStream') ? true : false; - scope.activityStreamTooltip = (toState.name === 'activityStream') ? 'Hide Activity Stream' : 'View Activity Stream'; + scope.activityStreamTooltip = (toState.name === 'activityStream') ? i18n._('Hide Activity Stream') : i18n._('View Activity Stream'); scope.showActivityStreamButton = (FeaturesService.featureEnabled('activity_streams') || toState.name ==='activityStream') ? true : false; } } diff --git a/awx/ui/client/src/configuration/configuration.route.js b/awx/ui/client/src/configuration/configuration.route.js index 7bf829ab53..991c052971 100644 --- a/awx/ui/client/src/configuration/configuration.route.js +++ b/awx/ui/client/src/configuration/configuration.route.js @@ -12,6 +12,7 @@ import ConfigurationJobsController from './jobs-form/configuration-jobs.controller'; import ConfigurationSystemController from './system-form/configuration-system.controller'; import ConfigurationUiController from './ui-form/configuration-ui.controller'; + import { N_ } from '../i18n'; export default { name: 'configuration', @@ -26,7 +27,7 @@ }, ncyBreadcrumb: { parent: 'setup', - label: "Edit Configuration" + label: N_("Edit Configuration") }, controller: ConfigurationController, resolve: { diff --git a/awx/ui/client/src/dashboard/hosts/main.js b/awx/ui/client/src/dashboard/hosts/main.js index 8b383ed5bb..84d04fa8ad 100644 --- a/awx/ui/client/src/dashboard/hosts/main.js +++ b/awx/ui/client/src/dashboard/hosts/main.js @@ -9,6 +9,7 @@ import form from './dashboard-hosts.form'; import listController from './dashboard-hosts-list.controller'; import editController from './dashboard-hosts-edit.controller'; import service from './dashboard-hosts.service'; +import { N_ } from '../../i18n'; export default angular.module('dashboardHosts', []) @@ -51,7 +52,7 @@ angular.module('dashboardHosts', []) }, ncyBreadcrumb: { parent: 'dashboard', - label: "HOSTS" + label: N_("HOSTS") }, }) }); diff --git a/awx/ui/client/src/helpers/ActivityStream.js b/awx/ui/client/src/helpers/ActivityStream.js index 37da4f2857..065d466ba5 100644 --- a/awx/ui/client/src/helpers/ActivityStream.js +++ b/awx/ui/client/src/helpers/ActivityStream.js @@ -12,45 +12,45 @@ export default angular.module('ActivityStreamHelper', ['Utilities']) - .factory('GetTargetTitle', [ - function () { + .factory('GetTargetTitle', ['i18n', + function (i18n) { return function (target) { - var rtnTitle = 'ALL ACTIVITY'; + var rtnTitle = i18n._('ALL ACTIVITY'); switch(target) { case 'project': - rtnTitle = 'PROJECTS'; + rtnTitle = i18n._('PROJECTS'); break; case 'inventory': - rtnTitle = 'INVENTORIES'; + rtnTitle = i18n._('INVENTORIES'); break; case 'credential': - rtnTitle = 'CREDENTIALS'; + rtnTitle = i18n._('CREDENTIALS'); break; case 'user': - rtnTitle = 'USERS'; + rtnTitle = i18n._('USERS'); break; case 'team': - rtnTitle = 'TEAMS'; + rtnTitle = i18n._('TEAMS'); break; case 'organization': - rtnTitle = 'ORGANIZATIONS'; + rtnTitle = i18n._('ORGANIZATIONS'); break; case 'job': - rtnTitle = 'JOBS'; + rtnTitle = i18n._('JOBS'); break; case 'inventory_script': - rtnTitle = 'INVENTORY SCRIPTS'; + rtnTitle = i18n._('INVENTORY SCRIPTS'); break; case 'schedule': - rtnTitle = 'SCHEDULES'; + rtnTitle = i18n._('SCHEDULES'); break; case 'host': - rtnTitle = 'HOSTS'; + rtnTitle = i18n._('HOSTS'); break; case 'template': - rtnTitle = 'TEMPLATES'; + rtnTitle = i18n._('TEMPLATES'); break; } diff --git a/awx/ui/client/src/inventories/main.js b/awx/ui/client/src/inventories/main.js index ac031283cd..fd5c8be173 100644 --- a/awx/ui/client/src/inventories/main.js +++ b/awx/ui/client/src/inventories/main.js @@ -12,6 +12,7 @@ import inventoryManageListRoute from './manage/inventory-manage.route'; import { copyMoveGroupRoute, copyMoveHostRoute } from './manage/copy-move/copy-move.route'; import adHocRoute from './manage/adhoc/adhoc.route'; import { templateUrl } from '../shared/template-url/template-url.factory'; +import { N_ } from '../i18n'; export default angular.module('inventory', [ inventoryAdd.name, @@ -55,7 +56,7 @@ angular.module('inventory', [ searchPrefix: 'schedule', ncyBreadcrumb: { parent: 'inventoryManage.editGroup({group_id: parentObject.id})', - label: 'SCHEDULES' + label: N_('SCHEDULES') }, resolve: { Dataset: ['SchedulesList', 'QuerySet', '$stateParams', 'GetBasePath', 'groupData', @@ -89,7 +90,7 @@ angular.module('inventory', [ '@': { templateProvider: function(SchedulesList, generateList, ParentObject) { // include name of parent resource in listTitle - SchedulesList.listTitle = `${ParentObject.name}
Schedules`; + SchedulesList.listTitle = `${ParentObject.name}
` + N_('Schedules'); let html = generateList.build({ list: SchedulesList, mode: 'edit' @@ -106,7 +107,7 @@ angular.module('inventory', [ name: 'inventoryManage.editGroup.schedules.add', url: '/add', ncyBreadcrumb: { - label: "CREATE SCHEDULE" + label: N_("CREATE SCHEDULE") }, views: { 'form': { diff --git a/awx/ui/client/src/inventories/manage/adhoc/adhoc.route.js b/awx/ui/client/src/inventories/manage/adhoc/adhoc.route.js index 51625b17ea..05a8d6dcad 100644 --- a/awx/ui/client/src/inventories/manage/adhoc/adhoc.route.js +++ b/awx/ui/client/src/inventories/manage/adhoc/adhoc.route.js @@ -5,6 +5,7 @@ *************************************************/ import {templateUrl} from '../../../shared/template-url/template-url.factory'; + import { N_ } from '../../../i18n'; export default { url: '/adhoc', @@ -22,6 +23,6 @@ export default { } }, ncyBreadcrumb: { - label: "RUN COMMAND" + label: N_("RUN COMMAND") } }; diff --git a/awx/ui/client/src/inventories/manage/copy-move/copy-move.route.js b/awx/ui/client/src/inventories/manage/copy-move/copy-move.route.js index b93e5e1e0f..c80d9b0591 100644 --- a/awx/ui/client/src/inventories/manage/copy-move/copy-move.route.js +++ b/awx/ui/client/src/inventories/manage/copy-move/copy-move.route.js @@ -4,6 +4,7 @@ * All Rights Reserved *************************************************/ import {templateUrl} from '../../../shared/template-url/template-url.factory'; +import { N_ } from '../../../i18n'; import CopyMoveGroupsController from './copy-move-groups.controller'; import CopyMoveHostsController from './copy-move-hosts.controller'; @@ -25,7 +26,7 @@ var copyMoveGroupRoute = { } }, ncyBreadcrumb: { - label: "COPY OR MOVE {{item.name}}" + label: N_("COPY OR MOVE") + " {{item.name}}" }, resolve: { Dataset: ['CopyMoveGroupList', 'QuerySet', '$stateParams', 'GetBasePath', 'group', @@ -61,7 +62,7 @@ var copyMoveHostRoute = { url: '/copy-move-host/{host_id}', searchPrefix: 'copy', ncyBreadcrumb: { - label: "COPY OR MOVE {{item.name}}" + label: N_("COPY OR MOVE") + " {{item.name}}" }, resolve: { Dataset: ['CopyMoveGroupList', 'QuerySet', '$stateParams', 'GetBasePath', diff --git a/awx/ui/client/src/inventory-scripts/main.js b/awx/ui/client/src/inventory-scripts/main.js index fc979e0592..f9910640a2 100644 --- a/awx/ui/client/src/inventory-scripts/main.js +++ b/awx/ui/client/src/inventory-scripts/main.js @@ -9,6 +9,7 @@ import inventoryScriptsAdd from './add/main'; import inventoryScriptsEdit from './edit/main'; import list from './inventory-scripts.list'; import form from './inventory-scripts.form'; +import { N_ } from '../i18n'; export default angular.module('inventoryScripts', [ @@ -62,7 +63,7 @@ angular.module('inventoryScripts', [ }, ncyBreadcrumb: { parent: 'setup', - label: 'INVENTORY SCRIPTS' + label: N_('INVENTORY SCRIPTS') } }) }); diff --git a/awx/ui/client/src/license/license.route.js b/awx/ui/client/src/license/license.route.js index 947820a6e1..78ab6e4348 100644 --- a/awx/ui/client/src/license/license.route.js +++ b/awx/ui/client/src/license/license.route.js @@ -5,6 +5,7 @@ *************************************************/ import {templateUrl} from '../shared/template-url/template-url.factory'; +import { N_ } from '../i18n'; export default { name: 'license', @@ -14,7 +15,7 @@ export default { data: {}, ncyBreadcrumb: { parent: 'setup', - label: 'LICENSE' + label: N_('LICENSE') }, resolve: { features: ['CheckLicense', '$rootScope', diff --git a/awx/ui/client/src/lists/Schedules.js b/awx/ui/client/src/lists/Schedules.js index fbf03f5678..343f3c472d 100644 --- a/awx/ui/client/src/lists/Schedules.js +++ b/awx/ui/client/src/lists/Schedules.js @@ -7,7 +7,8 @@ export default angular.module('SchedulesListDefinition', []) - .value('SchedulesList', { + .factory('StreamList', ['i18n', function(i18n) { + return { name: 'schedules', iterator: 'schedule', @@ -30,22 +31,22 @@ export default }, name: { key: true, - label: 'Name', + label: i18n._('Name'), ngClick: "editSchedule(schedule)", columnClass: "col-md-3 col-sm-3 col-xs-6" }, dtstart: { - label: 'First Run', + label: i18n._('First Run'), filter: "longDate", columnClass: "List-staticColumn--schedulerTime hidden-sm hidden-xs" }, next_run: { - label: 'Next Run', + label: i18n._('Next Run'), filter: "longDate", columnClass: "List-staticColumn--schedulerTime hidden-xs" }, dtend: { - label: 'Final Run', + label: i18n._('Final Run'), filter: "longDate", columnClass: "List-staticColumn--schedulerTime hidden-xs" }, @@ -54,45 +55,45 @@ export default actions: { refresh: { mode: 'all', - awToolTip: "Refresh the page", + awToolTip: i18n._("Refresh the page"), ngClick: "refreshSchedules()", actionClass: 'btn List-buttonDefault', ngShow: "socketStatus == 'error'", - buttonContent: 'REFRESH' + buttonContent: i18n._('REFRESH') }, add: { mode: 'all', ngClick: 'addSchedule()', - awToolTip: 'Add a new schedule', + awToolTip: i18n._('Add a new schedule'), actionClass: 'btn List-buttonSubmit', - buttonContent: '+ ADD', + buttonContent: '+ ' + i18n._('ADD'), ngShow: 'canAdd' } }, fieldActions: { edit: { - label: 'Edit', + label: i18n._('Edit'), ngClick: "editSchedule(schedule)", icon: 'icon-edit', - awToolTip: 'Edit schedule', + awToolTip: i18n._('Edit schedule'), dataPlacement: 'top', ngShow: 'schedule.summary_fields.user_capabilities.edit' }, view: { - label: 'View', + label: i18n._('View'), ngClick: "editSchedule(schedule)", - awToolTip: 'View schedule', + awToolTip: i18n._('View schedule'), dataPlacement: 'top', ngShow: '!schedule.summary_fields.user_capabilities.edit' }, "delete": { - label: 'Delete', + label: i18n._('Delete'), ngClick: "deleteSchedule(schedule.id)", icon: 'icon-trash', - awToolTip: 'Delete schedule', + awToolTip: i18n._('Delete schedule'), dataPlacement: 'top', ngShow: 'schedule.summary_fields.user_capabilities.delete' } } - }); + };}]); diff --git a/awx/ui/client/src/lists/Streams.js b/awx/ui/client/src/lists/Streams.js index 34e0652885..9604a89302 100644 --- a/awx/ui/client/src/lists/Streams.js +++ b/awx/ui/client/src/lists/Streams.js @@ -7,15 +7,16 @@ export default angular.module('StreamListDefinition', []) - .value('StreamList', { + .factory('StreamList', ['i18n', function(i18n) { + return { name: 'activities', iterator: 'activity', basePath: 'activity_stream', - editTitle: 'Activity Stream', - listTitle: 'Activity Stream
{{streamSubTitle}}', + editTitle: i18n._('Activity Stream'), + listTitle: 'Activity Stream
{{streamSubTitle}}', listTitleBadge: false, - emptyListText: 'There are no events to display at this time', + emptyListText: i18n._('There are no events to display at this time'), selectInstructions: '', index: false, hover: true, @@ -24,7 +25,7 @@ export default fields: { timestamp: { - label: 'Time', + label: i18n._('Time'), key: true, desc: true, noLink: true, @@ -32,14 +33,14 @@ export default columnClass: 'col-lg-3 col-md-2 col-sm-3 col-xs-3' }, user: { - label: 'Initiated by', + label: i18n._('Initiated by'), ngBindHtml: 'activity.user', // @todo punch monkey sourceModel: 'actor', sourceField: 'username', columnClass: 'col-lg-3 col-md-3 col-sm-3 col-xs-3' }, description: { - label: 'Event', + label: i18n._('Event'), ngBindHtml: 'activity.description', // @todo punch monkey nosort: true, columnClass: 'ActivityStream-eventColumnHeader col-lg-5 col-md-6 col-sm-4 col-xs-4' @@ -50,7 +51,7 @@ export default refresh: { mode: 'all', id: 'activity-stream-refresh-btn', - awToolTip: "Refresh the page", + awToolTip: i18n._("Refresh the page"), ngClick: "refreshStream()", actionClass: 'btn List-buttonDefault ActivityStream-refreshButton', buttonContent: 'REFRESH' @@ -62,13 +63,13 @@ export default columnClass: 'col-lg-1 col-md-1 col-sm-2 col-xs-2', view: { - label: 'View', + label: i18n._('View'), ngClick: "showDetail(activity.id)", icon: 'fa-zoom-in', "class": 'btn-default btn-xs', - awToolTip: 'View event details', + awToolTip: i18n._('View event details'), dataPlacement: 'top' } } - }); + };}]); diff --git a/awx/ui/client/src/login/loginModal/loginModal.partial.html b/awx/ui/client/src/login/loginModal/loginModal.partial.html index d6e30a20a3..eb124adcf4 100644 --- a/awx/ui/client/src/login/loginModal/loginModal.partial.html +++ b/awx/ui/client/src/login/loginModal/loginModal.partial.html @@ -86,7 +86,7 @@
-
NOTICE
{{ customLoginInfo | sanitize }}
+
NOTICE
{{ customLoginInfo | sanitize }}
stateDefinitions.generateTree({ parent: 'notifications', // top-most node in the generated tree @@ -84,7 +85,7 @@ angular.module('notifications', [ }, ncyBreadcrumb: { parent: 'setup', - name: 'NOTIFICATIONS' + name: N_('NOTIFICATIONS') } }) }); diff --git a/awx/ui/client/src/organizations/linkout/addUsers/addUsers.partial.html b/awx/ui/client/src/organizations/linkout/addUsers/addUsers.partial.html index df0d5233c6..a8a6cbbb81 100644 --- a/awx/ui/client/src/organizations/linkout/addUsers/addUsers.partial.html +++ b/awx/ui/client/src/organizations/linkout/addUsers/addUsers.partial.html @@ -5,7 +5,7 @@
-
{{ $parent.organization_name }}
Add {{ addType | capitalize}} +
{{ $parent.organization_name }}
Add {{ addType | capitalize}}
diff --git a/awx/ui/client/src/organizations/linkout/organizations-linkout.route.js b/awx/ui/client/src/organizations/linkout/organizations-linkout.route.js index ceb8a45251..517e37c8c0 100644 --- a/awx/ui/client/src/organizations/linkout/organizations-linkout.route.js +++ b/awx/ui/client/src/organizations/linkout/organizations-linkout.route.js @@ -10,6 +10,7 @@ import OrganizationsJobTemplates from './controllers/organizations-job-templates import OrganizationsProjects from './controllers/organizations-projects.controller'; import OrganizationsTeams from './controllers/organizations-teams.controller'; import OrganizationsUsers from './controllers/organizations-users.controller'; +import { N_ } from '../../i18n'; export default [{ name: 'organizations.users', @@ -55,7 +56,7 @@ export default [{ }, ncyBreadcrumb: { parent: "organizations.edit", - label: "USERS" + label: N_("USERS") }, data: { @@ -128,7 +129,7 @@ export default [{ }, ncyBreadcrumb: { parent: "organizations.edit", - label: "TEAMS" + label: N_("TEAMS") }, resolve: { features: ['FeaturesService', function(FeaturesService) { @@ -174,7 +175,7 @@ export default [{ }, ncyBreadcrumb: { parent: "organizations.edit", - label: "INVENTORIES" + label: N_("INVENTORIES") }, resolve: { features: ['FeaturesService', function(FeaturesService) { @@ -225,7 +226,7 @@ export default [{ }, ncyBreadcrumb: { parent: "organizations.edit", - label: "PROJECTS" + label: N_("PROJECTS") }, resolve: { features: ['FeaturesService', function(FeaturesService) { @@ -283,7 +284,7 @@ export default [{ }, ncyBreadcrumb: { parent: "organizations.edit", - label: "JOB TEMPLATES" + label: N_("JOB TEMPLATES") }, resolve: { features: ['FeaturesService', function(FeaturesService) { @@ -359,7 +360,7 @@ export default [{ }, ncyBreadcrumb: { parent: "organizations.edit", - label: "ADMINS" + label: N_("ADMINS") }, resolve: { features: ['FeaturesService', function(FeaturesService) { diff --git a/awx/ui/client/src/organizations/main.js b/awx/ui/client/src/organizations/main.js index fac6a179e9..076c89d1aa 100644 --- a/awx/ui/client/src/organizations/main.js +++ b/awx/ui/client/src/organizations/main.js @@ -10,6 +10,7 @@ import OrganizationsAdd from './add/organizations-add.controller'; import OrganizationsEdit from './edit/organizations-edit.controller'; import organizationsLinkout from './linkout/main'; import OrganizationsLinkoutStates from './linkout/organizations-linkout.route'; +import { N_ } from '../i18n'; export default @@ -48,7 +49,7 @@ angular.module('Organizations', [ }, ncyBreadcrumb: { parent: 'setup', - label: 'ORGANIZATIONS' + label: N_('ORGANIZATIONS') }, // concat manually-defined state definitions with generated defintions }).then((generated) => { diff --git a/awx/ui/client/src/portal-mode/portal-mode.route.js b/awx/ui/client/src/portal-mode/portal-mode.route.js index 403ace5795..4ae8d287af 100644 --- a/awx/ui/client/src/portal-mode/portal-mode.route.js +++ b/awx/ui/client/src/portal-mode/portal-mode.route.js @@ -1,6 +1,7 @@ import { templateUrl } from '../shared/template-url/template-url.factory'; import { PortalModeJobTemplatesController } from './portal-mode-job-templates.controller'; import { PortalModeJobsController } from './portal-mode-jobs.controller'; +import { N_ } from '../i18n'; // Using multiple named views requires a parent layout // https://github.com/angular-ui/ui-router/wiki/Multiple-Named-Views @@ -8,7 +9,7 @@ export default { name: 'portalMode', url: '/portal?{group_search:queryset}{host_search:queryset}', ncyBreadcrumb: { - label: 'MY VIEW' + label: N_('MY VIEW') }, params: { job_search: { diff --git a/awx/ui/client/src/scheduler/main.js b/awx/ui/client/src/scheduler/main.js index 9e04feae66..d1c44f6e09 100644 --- a/awx/ui/client/src/scheduler/main.js +++ b/awx/ui/client/src/scheduler/main.js @@ -9,6 +9,7 @@ import addController from './schedulerAdd.controller'; import editController from './schedulerEdit.controller'; import {templateUrl} from '../shared/template-url/template-url.factory'; import schedulerDatePicker from './schedulerDatePicker.directive'; +import { N_ } from '../i18n'; export default angular.module('scheduler', []) @@ -32,7 +33,7 @@ export default }, ncyBreadcrumb: { parent: 'templates.editJobTemplate({job_template_id: parentObject.id})', - label: 'SCHEDULES' + label: N_('SCHEDULES') }, resolve: { Dataset: ['SchedulesList', 'QuerySet', '$stateParams', 'GetBasePath', @@ -63,7 +64,7 @@ export default '@': { templateProvider: function(SchedulesList, generateList, ParentObject){ // include name of parent resource in listTitle - SchedulesList.listTitle = `${ParentObject.name}
Schedules`; + SchedulesList.listTitle = `${ParentObject.name}
` + N_('Schedules'); let html = generateList.build({ list: SchedulesList, mode: 'edit' @@ -86,7 +87,7 @@ export default }, ncyBreadcrumb: { parent: 'jobTemplateSchedules', - label: 'CREATE SCHEDULE' + label: N_('CREATE SCHEDULE') } }); $stateExtender.addState({ @@ -118,7 +119,7 @@ export default }, ncyBreadcrumb: { parent: 'templates.editWorkflowJobTemplate({workflow_job_template_id: parentObject.id})', - label: 'SCHEDULES' + label: N_('SCHEDULES') }, resolve: { Dataset: ['SchedulesList', 'QuerySet', '$stateParams', 'GetBasePath', @@ -149,7 +150,7 @@ export default '@': { templateProvider: function(SchedulesList, generateList, ParentObject){ // include name of parent resource in listTitle - SchedulesList.listTitle = `${ParentObject.name}
Schedules`; + SchedulesList.listTitle = `${ParentObject.name}
` + N_('Schedules'); let html = generateList.build({ list: SchedulesList, mode: 'edit' @@ -172,7 +173,7 @@ export default }, ncyBreadcrumb: { parent: 'workflowJobTemplateSchedules', - label: 'CREATE SCHEDULE' + label: N_('CREATE SCHEDULE') } }); $stateExtender.addState({ @@ -201,7 +202,7 @@ export default }, ncyBreadcrumb: { parent: 'projects.edit({project_id: parentObject.id})', - label: 'SCHEDULES' + label: N_('SCHEDULES') }, resolve: { Dataset: ['SchedulesList', 'QuerySet', '$stateParams', 'GetBasePath', @@ -232,7 +233,7 @@ export default '@': { templateProvider: function(SchedulesList, generateList, ParentObject){ // include name of parent resource in listTitle - SchedulesList.listTitle = `${ParentObject.name}
Schedules`; + SchedulesList.listTitle = `${ParentObject.name}
` + N_('Schedules'); let html = generateList.build({ list: SchedulesList, mode: 'edit' @@ -249,7 +250,7 @@ export default name: 'projectSchedules.add', route: '/add', ncyBreadcrumb: { - label: 'CREATE SCHEDULE' + label: N_('CREATE SCHEDULE') }, views: { 'form': { @@ -289,7 +290,7 @@ export default }, ncyBreadcrumb: { parent: 'jobs', - label: 'SCHEDULED' + label: N_('SCHEDULED') }, resolve: { SchedulesList: ['ScheduledJobsList', function(list){ diff --git a/awx/ui/client/src/setup-menu/setup.route.js b/awx/ui/client/src/setup-menu/setup.route.js index aac58a64cb..4268f38087 100644 --- a/awx/ui/client/src/setup-menu/setup.route.js +++ b/awx/ui/client/src/setup-menu/setup.route.js @@ -1,10 +1,11 @@ import {templateUrl} from '../shared/template-url/template-url.factory'; +import { N_ } from '../i18n'; export default { name: 'setup', route: '/setup', ncyBreadcrumb: { - label: "SETTINGS" + label: N_("SETTINGS") }, templateUrl: templateUrl('setup-menu/setup-menu'), controller: function(orgAdmin, $scope){ diff --git a/awx/ui/client/src/shared/paginate/paginate.partial.html b/awx/ui/client/src/shared/paginate/paginate.partial.html index cb085c45ec..e2249ae7b0 100644 --- a/awx/ui/client/src/shared/paginate/paginate.partial.html +++ b/awx/ui/client/src/shared/paginate/paginate.partial.html @@ -30,15 +30,15 @@ - Page + Page {{current()}} of {{last()}}
- ITEMS  + ITEMS  {{dataRange}} - of {{dataCount()}} + of {{dataCount()}}
diff --git a/awx/ui/client/src/shared/smart-search/smart-search.controller.js b/awx/ui/client/src/shared/smart-search/smart-search.controller.js index 6644943f0a..5d60842674 100644 --- a/awx/ui/client/src/shared/smart-search/smart-search.controller.js +++ b/awx/ui/client/src/shared/smart-search/smart-search.controller.js @@ -1,5 +1,5 @@ -export default ['$stateParams', '$scope', '$state', 'QuerySet', 'GetBasePath', 'QuerySet', 'SmartSearchService', - function($stateParams, $scope, $state, QuerySet, GetBasePath, qs, SmartSearchService) { +export default ['$stateParams', '$scope', '$state', 'QuerySet', 'GetBasePath', 'QuerySet', 'SmartSearchService', 'i18n', + function($stateParams, $scope, $state, QuerySet, GetBasePath, qs, SmartSearchService, i18n) { let path, relations, defaults, @@ -35,6 +35,7 @@ export default ['$stateParams', '$scope', '$state', 'QuerySet', 'GetBasePath', ' $scope.options = data.options.data; $scope.$emit(`${$scope.list.iterator}_options`, data.options); }); + $scope.searchPlaceholder = $scope.disableSearch ? i18n._('Cannot search running job') : i18n._('Search'); function compareParams(a, b) { for (let key in a) { @@ -73,6 +74,15 @@ export default ['$stateParams', '$scope', '$state', 'QuerySet', 'GetBasePath', ' }); $scope.$on('$destroy', stateChangeSuccessListener); + + $scope.$watch('disableSearch', function(disableSearch){ + if(disableSearch) { + $scope.searchPlaceholder = i18n._('Cannot search running job'); + } + else { + $scope.searchPlaceholder = i18n._('Search'); + } + }); } // Removes state definition defaults and pagination terms diff --git a/awx/ui/client/src/shared/smart-search/smart-search.partial.html b/awx/ui/client/src/shared/smart-search/smart-search.partial.html index 378d277391..b19b011698 100644 --- a/awx/ui/client/src/shared/smart-search/smart-search.partial.html +++ b/awx/ui/client/src/shared/smart-search/smart-search.partial.html @@ -4,14 +4,14 @@
-
-
+
Key
@@ -28,7 +28,7 @@ {{tag}}
- CLEAR ALL + CLEAR ALL
@@ -37,7 +37,7 @@
- EXAMPLES: + EXAMPLES:
@@ -45,13 +45,13 @@
- FIELDS: {{ key }}, + FIELDS: {{ key }},
- RELATED FIELDS: {{ relation }}, + RELATED FIELDS: {{ relation }},
- ADDITIONAL INFORMATION: For additional information on advanced search search syntax please see the Ansible Tower documentation. + ADDITIONAL INFORMATION: For additional information on advanced search search syntax please see the Ansible Tower documentation.
diff --git a/awx/ui/client/src/shared/stateDefinitions.factory.js b/awx/ui/client/src/shared/stateDefinitions.factory.js index 8279173dc9..16b317f32f 100644 --- a/awx/ui/client/src/shared/stateDefinitions.factory.js +++ b/awx/ui/client/src/shared/stateDefinitions.factory.js @@ -9,6 +9,8 @@ * generateLookupNodes - Attaches to a form node. Builds an abstract '*.lookup' node with field-specific 'lookup.*' children e.g. {name: 'projects.add.lookup.organizations', ...} */ + import { N_ } from '../i18n'; + export default ['$injector', '$stateExtender', '$log', function($injector, $stateExtender, $log) { return { /** @@ -150,7 +152,7 @@ export default ['$injector', '$stateExtender', '$log', function($injector, $stat url: url, ncyBreadcrumb: { [params.parent ? 'parent' : null]: `${params.parent}`, - label: `CREATE ${form.breadcrumbName || form.name}` + label: N_('CREATE') + ` ${form.breadcrumbName || form.name}` }, views: { 'form': { @@ -492,7 +494,7 @@ export default ['$injector', '$stateExtender', '$log', function($injector, $stat // } return state; } - + function buildRbacUserDirective() { let states = []; diff --git a/awx/ui/client/src/system-tracking/system-tracking.route.js b/awx/ui/client/src/system-tracking/system-tracking.route.js index 6875df68fa..da803afc91 100644 --- a/awx/ui/client/src/system-tracking/system-tracking.route.js +++ b/awx/ui/client/src/system-tracking/system-tracking.route.js @@ -5,6 +5,7 @@ *************************************************/ import {templateUrl} from '../shared/template-url/template-url.factory'; +import { N_ } from '../i18n'; export default { name: 'systemTracking', @@ -14,7 +15,7 @@ export default { params: {hosts: null, inventory: null}, reloadOnSearch: false, ncyBreadcrumb: { - label: "SYSTEM TRACKING" + label: N_("SYSTEM TRACKING") }, resolve: { moduleOptions: diff --git a/awx/ui/client/src/templates/list/templates-list.route.js b/awx/ui/client/src/templates/list/templates-list.route.js index e615e52db1..6846620d17 100644 --- a/awx/ui/client/src/templates/list/templates-list.route.js +++ b/awx/ui/client/src/templates/list/templates-list.route.js @@ -4,11 +4,13 @@ * All Rights Reserved *************************************************/ + import { N_ } from '../../i18n'; + export default { name: 'templates', route: '/templates', ncyBreadcrumb: { - label: "TEMPLATES" + label: N_("TEMPLATES") }, data: { activityStream: true, From db80f1257981ee5f50c5051d0221c478c0b3366f Mon Sep 17 00:00:00 2001 From: Michael Abashian Date: Sat, 28 Jan 2017 11:00:39 -0500 Subject: [PATCH 011/178] Truncate inventory manage breadcrumbs --- awx/ui/client/src/bread-crumb/bread-crumb.block.less | 4 ++++ .../manage/breadcrumbs/breadcrumbs.partial.html | 10 +++++----- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/awx/ui/client/src/bread-crumb/bread-crumb.block.less b/awx/ui/client/src/bread-crumb/bread-crumb.block.less index e0abee7832..e2254f6fa2 100644 --- a/awx/ui/client/src/bread-crumb/bread-crumb.block.less +++ b/awx/ui/client/src/bread-crumb/bread-crumb.block.less @@ -73,6 +73,10 @@ vertical-align: bottom; } +.BreadCrumb-invItem { + max-width: 400px; +} + .BreadCrumb-item + .BreadCrumb-item:before { content: "/"; padding: 0 5px; diff --git a/awx/ui/client/src/inventories/manage/breadcrumbs/breadcrumbs.partial.html b/awx/ui/client/src/inventories/manage/breadcrumbs/breadcrumbs.partial.html index 702a85379e..0444f0f662 100644 --- a/awx/ui/client/src/inventories/manage/breadcrumbs/breadcrumbs.partial.html +++ b/awx/ui/client/src/inventories/manage/breadcrumbs/breadcrumbs.partial.html @@ -1,22 +1,22 @@ From 75d89fcda19995f5eec6864d2cb0a2c9eb1b27d7 Mon Sep 17 00:00:00 2001 From: Michael Abashian Date: Sat, 28 Jan 2017 11:45:08 -0500 Subject: [PATCH 012/178] Add workflow job templates to the recently used templates list on the dashboard --- awx/ui/client/src/controllers/Home.js | 4 +-- .../job-templates-list.directive.js | 25 ++++++++++++++++--- .../job-templates-list.partial.html | 4 +-- 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/awx/ui/client/src/controllers/Home.js b/awx/ui/client/src/controllers/Home.js index a4999487f6..3c81e7a798 100644 --- a/awx/ui/client/src/controllers/Home.js +++ b/awx/ui/client/src/controllers/Home.js @@ -47,7 +47,7 @@ export function Home($scope, $compile, $stateParams, $rootScope, $location, $log ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to get dashboard jobs list: ' + status }); }); - Rest.setUrl(GetBasePath("job_templates") + "?order_by=-last_job_run&page_size=5&last_job_run__isnull=false"); + Rest.setUrl(GetBasePath("unified_job_templates") + "?order_by=-last_job_run&page_size=5&last_job_run__isnull=false&type=workflow_job_template,job_template"); Rest.get() .success(function (data) { $scope.dashboardJobTemplatesListData = data.results; @@ -123,7 +123,7 @@ export function Home($scope, $compile, $stateParams, $rootScope, $location, $log .error(function (data, status) { ProcessErrors($scope, data, status, null, { hdr: 'Error!', msg: 'Failed to get dashboard jobs list: ' + status }); }); - Rest.setUrl(GetBasePath("job_templates") + "?order_by=-last_job_run&page_size=5&last_job_run__isnull=false"); + Rest.setUrl(GetBasePath("unified_job_templates") + "?order_by=-last_job_run&page_size=5&last_job_run__isnull=false&type=workflow_job_template,job_template"); Rest.get() .success(function (data) { data = data.results; diff --git a/awx/ui/client/src/dashboard/lists/job-templates/job-templates-list.directive.js b/awx/ui/client/src/dashboard/lists/job-templates/job-templates-list.directive.js index 82aca5b377..13fbfd8d6f 100644 --- a/awx/ui/client/src/dashboard/lists/job-templates/job-templates-list.directive.js +++ b/awx/ui/client/src/dashboard/lists/job-templates/job-templates-list.directive.js @@ -3,7 +3,8 @@ export default [ 'InitiatePlaybookRun', 'templateUrl', '$state', - function JobTemplatesList(InitiatePlaybookRun, templateUrl, $state) { + 'Alert', + function JobTemplatesList(InitiatePlaybookRun, templateUrl, $state, Alert) { return { restrict: 'E', link: link, @@ -32,7 +33,8 @@ export default launch_url: job_template.url, edit_url: job_template.url.replace('api/v1', '#'), name: job_template.name, - id: job_template.id + id: job_template.id, + type: job_template.type }; }); scope.snapRows = (list.length < 4); @@ -42,8 +44,23 @@ export default return (status === "successful"); }; - scope.launchJobTemplate = function(jobTemplateId){ - InitiatePlaybookRun({ scope: scope, id: jobTemplateId, job_type: 'job_template' }); + scope.launchJobTemplate = function(template){ + if(template) { + if(template.type && (template.type === 'Job Template' || template.type === 'job_template')) { + InitiatePlaybookRun({ scope: scope, id: template.id, job_type: 'job_template' }); + } + else if(template.type && (template.type === 'Workflow Job Template' || template.type === 'workflow_job_template')) { + InitiatePlaybookRun({ scope: scope, id: template.id, job_type: 'workflow_job_template' }); + } + else { + // Something went wrong - Let the user know that we're unable to launch because we don't know + // what type of job template this is + Alert('Error: Unable to determine template type', 'We were unable to determine this template\'s type while launching.'); + } + } + else { + Alert('Error: Unable to launch template', 'Template parameter is missing'); + } }; scope.editJobTemplate = function (jobTemplateId) { diff --git a/awx/ui/client/src/dashboard/lists/job-templates/job-templates-list.partial.html b/awx/ui/client/src/dashboard/lists/job-templates/job-templates-list.partial.html index 8b404d9076..cffd21ce5c 100644 --- a/awx/ui/client/src/dashboard/lists/job-templates/job-templates-list.partial.html +++ b/awx/ui/client/src/dashboard/lists/job-templates/job-templates-list.partial.html @@ -1,7 +1,7 @@

- RECENTLY USED JOB TEMPLATES + RECENTLY USED TEMPLATES

VIEW ALL @@ -34,7 +34,7 @@
-
diff --git a/awx/ui/client/src/access/rbac-multiselect/permissionsTeams.list.js b/awx/ui/client/src/access/rbac-multiselect/permissionsTeams.list.js index 8986478e85..dd05b4b0cc 100644 --- a/awx/ui/client/src/access/rbac-multiselect/permissionsTeams.list.js +++ b/awx/ui/client/src/access/rbac-multiselect/permissionsTeams.list.js @@ -5,7 +5,7 @@ *************************************************/ - export default function() { + export default ['i18n', function(i18n) { return { searchSize: 'col-lg-12 col-md-12 col-sm-12 col-xs-12', name: 'teams', @@ -15,19 +15,20 @@ multiSelectExtended: true, index: false, hover: true, - emptyListText : 'No Teams exist', + emptyListText : i18n._('No Teams exist'), fields: { name: { key: true, - label: 'name' + label: i18n._('name') }, organization: { - label: 'organization', + label: i18n._('organization'), ngBind: 'team.summary_fields.organization.name', sourceModel: 'organization', - sourceField: 'name' + sourceField: 'name', + searchable: true } } }; -} +}]; diff --git a/awx/ui/client/src/access/rbac-multiselect/permissionsUsers.list.js b/awx/ui/client/src/access/rbac-multiselect/permissionsUsers.list.js index 58a5605281..9769df3506 100644 --- a/awx/ui/client/src/access/rbac-multiselect/permissionsUsers.list.js +++ b/awx/ui/client/src/access/rbac-multiselect/permissionsUsers.list.js @@ -5,7 +5,7 @@ *************************************************/ - export default function() { + export default ['i18n', function(i18n) { return { name: 'users', iterator: 'user', @@ -21,22 +21,22 @@ multiSelectExtended: true, index: false, hover: true, - emptyListText : 'No Users exist', + emptyListText : i18n._('No Users exist'), fields: { 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' }, username: { key: true, - label: 'Username', + label: i18n._('Username'), columnClass: 'col-md-5 col-sm-5 col-xs-11' }, }, }; -} +}]; diff --git a/awx/ui/client/src/access/rbac-multiselect/rbac-multiselect-role.directive.js b/awx/ui/client/src/access/rbac-multiselect/rbac-multiselect-role.directive.js index 99e3a1d8ed..f5276dbc12 100644 --- a/awx/ui/client/src/access/rbac-multiselect/rbac-multiselect-role.directive.js +++ b/awx/ui/client/src/access/rbac-multiselect/rbac-multiselect-role.directive.js @@ -8,7 +8,8 @@ export default [ 'CreateSelect2', - function(CreateSelect2) { + 'i18n', + function(CreateSelect2, i18n) { return { restrict: 'E', scope: { @@ -21,7 +22,7 @@ export default CreateSelect2({ element: '.roleSelect2', multiple: true, - placeholder: 'Select roles' + placeholder: i18n._('Select roles') }); } }; diff --git a/awx/ui/client/src/activity-stream/streamDetailModal/streamDetailModal.partial.html b/awx/ui/client/src/activity-stream/streamDetailModal/streamDetailModal.partial.html index 67e4452ebc..9735fd6e17 100644 --- a/awx/ui/client/src/activity-stream/streamDetailModal/streamDetailModal.partial.html +++ b/awx/ui/client/src/activity-stream/streamDetailModal/streamDetailModal.partial.html @@ -9,15 +9,15 @@
diff --git a/awx/ui/client/src/activity-stream/streamDropdownNav/stream-dropdown-nav.directive.js b/awx/ui/client/src/activity-stream/streamDropdownNav/stream-dropdown-nav.directive.js index dc6c4a819d..c8cfc37147 100644 --- a/awx/ui/client/src/activity-stream/streamDropdownNav/stream-dropdown-nav.directive.js +++ b/awx/ui/client/src/activity-stream/streamDropdownNav/stream-dropdown-nav.directive.js @@ -4,7 +4,7 @@ * All Rights Reserved *************************************************/ -export default ['templateUrl', function(templateUrl) { +export default ['templateUrl', 'i18n', function(templateUrl, i18n) { return { restrict: 'E', scope: true, @@ -15,18 +15,18 @@ export default ['templateUrl', function(templateUrl) { $scope.streamTarget = ($state.params && $state.params.target) ? $state.params.target : 'dashboard'; $scope.options = [ - {label: 'All Activity', value: 'dashboard'}, - {label: 'Credentials', value: 'credential'}, - {label: 'Hosts', value: 'host'}, - {label: 'Inventories', value: 'inventory'}, - {label: 'Inventory Scripts', value: 'inventory_script'}, - {label: 'Jobs', value: 'job'}, - {label: 'Organizations', value: 'organization'}, - {label: 'Projects', value: 'project'}, - {label: 'Schedules', value: 'schedule'}, - {label: 'Teams', value: 'team'}, - {label: 'Templates', value: 'template'}, - {label: 'Users', value: 'user'} + {label: i18n._('All Activity'), value: 'dashboard'}, + {label: i18n._('Credentials'), value: 'credential'}, + {label: i18n._('Hosts'), value: 'host'}, + {label: i18n._('Inventories'), value: 'inventory'}, + {label: i18n._('Inventory Scripts'), value: 'inventory_script'}, + {label: i18n._('Jobs'), value: 'job'}, + {label: i18n._('Organizations'), value: 'organization'}, + {label: i18n._('Projects'), value: 'project'}, + {label: i18n._('Schedules'), value: 'schedule'}, + {label: i18n._('Teams'), value: 'team'}, + {label: i18n._('Templates'), value: 'template'}, + {label: i18n._('Users'), value: 'user'} ]; CreateSelect2({ diff --git a/awx/ui/client/src/app.js b/awx/ui/client/src/app.js index 577ce739b8..4cf399699d 100644 --- a/awx/ui/client/src/app.js +++ b/awx/ui/client/src/app.js @@ -99,6 +99,8 @@ var tower = angular.module('Tower', [ require('angular-tz-extensions'), require('lr-infinite-scroll'), require('ng-toast'), + 'gettext', + 'I18N', uiRouter, 'ui.router.state.events', @@ -201,8 +203,6 @@ var tower = angular.module('Tower', [ scheduler.name, 'ApiModelHelper', 'ActivityStreamHelper', - 'gettext', - 'I18N', 'WorkflowFormDefinition', 'InventorySourcesListDefinition', 'WorkflowMakerFormDefinition' @@ -290,6 +290,9 @@ var tower = angular.module('Tower', [ "jobs": ["status_changed"] } } + }, + ncyBreadcrumb: { + label: N_('PROJECTS') } }) }); @@ -371,12 +374,12 @@ var tower = angular.module('Tower', [ 'CheckLicense', '$location', 'Authorization', 'LoadBasePaths', 'Timer', 'ClearScope', 'LoadConfig', 'Store', 'pendoService', 'Prompt', 'Rest', 'Wait', 'ProcessErrors', '$state', 'GetBasePath', 'ConfigService', - 'FeaturesService', '$filter', 'SocketService', 'I18NInit', + 'FeaturesService', '$filter', 'SocketService', function($stateExtender, $q, $compile, $cookieStore, $rootScope, $log, $stateParams, CheckLicense, $location, Authorization, LoadBasePaths, Timer, ClearScope, LoadConfig, Store, pendoService, Prompt, Rest, Wait, ProcessErrors, $state, GetBasePath, ConfigService, FeaturesService, - $filter, SocketService, I18NInit) { + $filter, SocketService) { $rootScope.$state = $state; $rootScope.$state.matches = function(stateName) { @@ -388,7 +391,6 @@ var tower = angular.module('Tower', [ $log.debug(`$state.defaultErrorHandler: ${error}`); }); - I18NInit(); $stateExtender.addState({ name: 'dashboard', url: '/home', diff --git a/awx/ui/client/src/configuration/auth-form/configuration-auth.partial.html b/awx/ui/client/src/configuration/auth-form/configuration-auth.partial.html index 71192e17c6..8004975a34 100644 --- a/awx/ui/client/src/configuration/auth-form/configuration-auth.partial.html +++ b/awx/ui/client/src/configuration/auth-form/configuration-auth.partial.html @@ -1,6 +1,6 @@
-
Sub Category
+
Sub Category
@@ -173,10 +173,10 @@
@@ -194,8 +194,8 @@
- - + +
@@ -205,9 +205,9 @@ - - - + + +
PlaysStartedElapsedPlaysStartedElapsed
@@ -242,7 +242,7 @@
-
2 Please select a task below to view its associated hosts
+
2 Please select a task below to view its associated hosts
@@ -253,8 +253,8 @@
- - + +
@@ -263,10 +263,10 @@ - - - - + + + +
TasksStartedElapsedTasksStartedElapsed
@@ -304,19 +304,19 @@ {{ task.missingCount }}
- No matching hosts. + No matching hosts.
- Waiting... + Waiting... - Loading... + Loading... - No matching tasks + No matching tasks @@ -337,8 +337,8 @@
- - + +
@@ -346,9 +346,9 @@ - - - + + +
HostsItemMessageHostsItemMessage
@@ -365,13 +365,13 @@ {{ result.msg }} - Waiting... + Waiting... - Loading... + Loading... - No matching host events + No matching host events @@ -388,10 +388,10 @@ @@ -411,13 +411,13 @@
-
STANDARD OUT
+
STANDARD OUT
- - diff --git a/awx/ui/client/src/job-submission/job-submission-factories/launchjob.factory.js b/awx/ui/client/src/job-submission/job-submission-factories/launchjob.factory.js index 64c9344b19..d49b4e521d 100644 --- a/awx/ui/client/src/job-submission/job-submission-factories/launchjob.factory.js +++ b/awx/ui/client/src/job-submission/job-submission-factories/launchjob.factory.js @@ -1,6 +1,6 @@ export default - function LaunchJob(Rest, Wait, ProcessErrors, ToJSON, Empty, GetBasePath, $state, $location, $rootScope) { + function LaunchJob(Rest, Wait, ProcessErrors, ToJSON, Empty, GetBasePath, $state, $location, $rootScope, i18n) { // This factory gathers up all the job launch data and POST's it. @@ -163,8 +163,10 @@ export default } }) .error(function(data, status) { - ProcessErrors(scope, data, status, null, { hdr: 'Error!', - msg: 'Failed updating job ' + scope.job_template_id + ' with variables. POST returned: ' + status }); + let template_id = scope.job_template_id; + template_id = (template_id === undefined) ? "undefined" : i18n.sprintf("%d", template_id); + ProcessErrors(scope, data, status, null, { hdr: i18n._('Error!'), + msg: i18n.sprintf(i18n._('Failed updating job %s with variables. POST returned: %d'), template_id, status) }); }); }; @@ -182,8 +184,8 @@ export default buildData(); }) .error(function (data, status) { - ProcessErrors(scope, data, status, { hdr: 'Error!', - msg: 'Failed to retrieve job template extra variables.' }); + ProcessErrors(scope, data, status, { hdr: i18n._('Error!'), + msg: i18n._('Failed to retrieve job template extra variables.') }); }); }; @@ -209,5 +211,6 @@ LaunchJob.$inject = 'GetBasePath', '$state', '$location', - '$rootScope' + '$rootScope', + 'i18n' ]; diff --git a/awx/ui/client/src/lists/AllJobs.js b/awx/ui/client/src/lists/AllJobs.js index d5e73c3982..4f09a4f501 100644 --- a/awx/ui/client/src/lists/AllJobs.js +++ b/awx/ui/client/src/lists/AllJobs.js @@ -13,7 +13,7 @@ export default name: 'jobs', basePath: 'unified_jobs', iterator: 'job', - editTitle: 'All Jobs', + editTitle: i18n._('All Jobs'), index: false, hover: true, well: false, @@ -42,13 +42,13 @@ export default noLink: true }, name: { - label: 'Name', + label: i18n._('Name'), columnClass: 'col-lg-2 col-md-3 col-sm-4 col-xs-6', ngClick: "viewJobDetails(job)", badgePlacement: 'right', badgeCustom: true, badgeIcon: ` ` }, type: { - label: 'Type', + label: i18n._('Type'), ngBind: 'job.type_label', link: false, columnClass: "col-lg-2 hidden-md hidden-sm hidden-xs", columnShow: "showJobType", }, finished: { - label: 'Finished', + label: i18n._('Finished'), noLink: true, filter: "longDate", columnClass: "col-lg-2 col-md-3 col-sm-3 hidden-xs", @@ -73,7 +73,7 @@ export default desc: true }, labels: { - label: 'Labels', + label: i18n._('Labels'), type: 'labels', nosort: true, showDelete: false, @@ -91,28 +91,28 @@ export default "view": { mode: "all", ngClick: "viewJobDetails(job)", - awToolTip: "View the job", + awToolTip: i18n._("View the job"), dataPlacement: "top" }, submit: { icon: 'icon-rocket', mode: 'all', ngClick: 'relaunchJob($event, job.id)', - awToolTip: 'Relaunch using the same parameters', + awToolTip: i18n._('Relaunch using the same parameters'), dataPlacement: 'top', ngShow: "!(job.type == 'system_job') && job.summary_fields.user_capabilities.start" }, cancel: { mode: 'all', ngClick: 'deleteJob(job.id)', - awToolTip: 'Cancel the job', + awToolTip: i18n._('Cancel the job'), dataPlacement: 'top', ngShow: "(job.status === 'running'|| job.status === 'waiting' || job.status === 'pending') && job.summary_fields.user_capabilities.start" }, "delete": { mode: 'all', ngClick: 'deleteJob(job.id)', - awToolTip: 'Delete the job', + awToolTip: i18n._('Delete the job'), dataPlacement: 'top', ngShow: "(job.status !== 'running' && job.status !== 'waiting' && job.status !== 'pending') && job.summary_fields.user_capabilities.delete" } diff --git a/awx/ui/client/src/lists/JobEvents.js b/awx/ui/client/src/lists/JobEvents.js index 9327ced8da..742fa354b6 100644 --- a/awx/ui/client/src/lists/JobEvents.js +++ b/awx/ui/client/src/lists/JobEvents.js @@ -7,11 +7,12 @@ export default angular.module('JobEventsListDefinition', []) - .value('JobEventList', { + .factory('JobEventList', ['i18n', function(i18n) { + return { name: 'jobevents', iterator: 'jobevent', - editTitle: 'Job Events', + editTitle: i18n._('Job Events'), index: false, hover: true, "class": "condensed", @@ -27,27 +28,27 @@ export default //}, events: { href: '/#/job_events/{{ job_id }}', - label: 'Events', + label: i18n._('Events'), active: true, icon: 'icon-list-ul' }, hosts: { href: '/#/job_host_summaries/{{ job_id }}', - label: 'Host Summary', + label: i18n._('Host Summary'), icon: 'icon-laptop' } }, fields: { created: { - label: 'Created On', + label: i18n._('Created On'), columnClass: 'col-lg-1 col-md-1 hidden-sm hidden-xs', key: true, nosort: true, noLink: true }, status: { - label: 'Status', + label: i18n._('Status'), showValue: false, columnClass: 'col-sm-1 col-xs-2 text-center', nosort: true, @@ -61,7 +62,7 @@ export default badgeNgClick: 'viewJobEvent(jobevent.id)' }, event_display: { - label: 'Event', + label: i18n._('Event'), hasChildren: true, ngClick: 'toggleChildren(jobevent.id, jobevent.related.children)', nosort: true, @@ -69,7 +70,7 @@ export default appendHTML: 'jobevent.event_detail' }, host: { - label: 'Host', + label: i18n._('Host'), ngBind: 'jobevent.summary_fields.host.name', ngHref: '{{ jobevent.hostLink }}', nosort: true, @@ -85,7 +86,7 @@ export default awToolTip: 'Refresh the page', ngClick: 'refresh()', actionClass: 'btn List-buttonDefault', - buttonContent: 'REFRESH' + buttonContent: i18n._('REFRESH') } }, @@ -94,10 +95,10 @@ export default columnClass: 'col-sm-1 col-xs-2', view: { - label: 'View', + label: i18n._('View'), ngClick: 'viewJobEvent(jobevent.id)', - awToolTip: 'View event details', + awToolTip: i18n._('View event details'), dataPlacement: 'top' } } - }); + };}]); diff --git a/awx/ui/client/src/lists/Streams.js b/awx/ui/client/src/lists/Streams.js index 9604a89302..bb2258a22e 100644 --- a/awx/ui/client/src/lists/Streams.js +++ b/awx/ui/client/src/lists/Streams.js @@ -14,7 +14,7 @@ export default iterator: 'activity', basePath: 'activity_stream', editTitle: i18n._('Activity Stream'), - listTitle: 'Activity Stream
{{streamSubTitle}}', + listTitle: i18n._('Activity Stream') + '
{{streamSubTitle}}', listTitleBadge: false, emptyListText: i18n._('There are no events to display at this time'), selectInstructions: '', @@ -54,7 +54,7 @@ export default awToolTip: i18n._("Refresh the page"), ngClick: "refreshStream()", actionClass: 'btn List-buttonDefault ActivityStream-refreshButton', - buttonContent: 'REFRESH' + buttonContent: i18n._('REFRESH') } }, diff --git a/awx/ui/client/src/login/authenticationServices/timer.factory.js b/awx/ui/client/src/login/authenticationServices/timer.factory.js index f68d0e412a..ebdf440baf 100644 --- a/awx/ui/client/src/login/authenticationServices/timer.factory.js +++ b/awx/ui/client/src/login/authenticationServices/timer.factory.js @@ -23,9 +23,9 @@ */ export default ['$rootScope', '$cookieStore', 'CreateDialog', 'Authorization', - 'Store', '$interval', '$state', '$q', + 'Store', '$interval', '$state', '$q', 'i18n', function ($rootScope, $cookieStore, CreateDialog, Authorization, - Store, $interval, $state, $q) { + Store, $interval, $state, $q, i18n) { return { sessionTime: null, @@ -154,7 +154,7 @@ export default }); CreateDialog({ id: 'idle-modal' , - title: "Idle Session", + title: i18n._("Idle Session"), scope: $rootScope, buttons: buttons, width: 470, diff --git a/awx/ui/client/src/management-jobs/card/card.controller.js b/awx/ui/client/src/management-jobs/card/card.controller.js index c3a8e71b04..19e521804f 100644 --- a/awx/ui/client/src/management-jobs/card/card.controller.js +++ b/awx/ui/client/src/management-jobs/card/card.controller.js @@ -10,11 +10,11 @@ export default [ 'Wait', '$compile', 'CreateDialog', 'GetBasePath' , 'SchedulesList', 'Rest' , 'ProcessErrors', 'managementJobsListObject', '$rootScope', '$state', - '$scope', 'CreateSelect2', + '$scope', 'CreateSelect2', 'i18n', function( Wait, $compile, CreateDialog, GetBasePath, SchedulesList, Rest, ProcessErrors, managementJobsListObject, $rootScope, $state, $scope, - CreateSelect2) { + CreateSelect2, i18n) { var defaultUrl = GetBasePath('system_job_templates') + "?order_by=name"; @@ -26,8 +26,8 @@ export default Wait('stop'); }) .error(function(data, status){ - ProcessErrors($scope, data, status, null, {hdr: 'Error!', - msg: 'Call to '+ defaultUrl + ' failed. Return status: '+ status}); + ProcessErrors($scope, data, status, null, {hdr: i18n._('Error!'), + msg: i18n.sprintf(i18n._('Call to %s failed. Return status: %d'), (defaultUrl === undefined) ? "undefined" : defaultUrl, status )}); }); }; getManagementJobs(); @@ -145,8 +145,10 @@ export default $state.go('managementJobStdout', {id: data.system_job}, {reload:true}); }) .error(function(data, status) { - ProcessErrors(scope, data, status, null, { hdr: 'Error!', - msg: 'Failed updating job ' + scope.job_template_id + ' with variables. POST returned: ' + status }); + let template_id = scope.job_template_id; + template_id = (template_id === undefined) ? "undefined" : i18n.sprintf("%d", template_id); + ProcessErrors(scope, data, status, null, { hdr: i18n._('Error!'), + msg: i18n.sprintf(i18n._('Failed updating job %s with variables. POST returned: %d'), template_id, status) }); }); }, "class": "btn btn-primary", @@ -233,8 +235,10 @@ export default $state.go('managementJobStdout', {id: data.system_job}, {reload:true}); }) .error(function(data, status) { - ProcessErrors(scope, data, status, null, { hdr: 'Error!', - msg: 'Failed updating job ' + scope.job_template_id + ' with variables. POST returned: ' + status }); + let template_id = scope.job_template_id; + template_id = (template_id === undefined) ? "undefined" : i18n.sprintf("%d", template_id); + ProcessErrors(scope, data, status, null, { hdr: i18n._('Error!'), + msg: i18n.sprintf(i18n._('Failed updating job %s with variables. POST returned: %d'), template_id, status) }); }); }, "class": "btn btn-primary", diff --git a/awx/ui/client/src/notifications/main.js b/awx/ui/client/src/notifications/main.js index 2b617ed95c..ce0d04429d 100644 --- a/awx/ui/client/src/notifications/main.js +++ b/awx/ui/client/src/notifications/main.js @@ -85,7 +85,7 @@ angular.module('notifications', [ }, ncyBreadcrumb: { parent: 'setup', - name: N_('NOTIFICATIONS') + label: N_('NOTIFICATIONS') } }) }); diff --git a/awx/ui/client/src/notifications/notification-templates-list/list.controller.js b/awx/ui/client/src/notifications/notification-templates-list/list.controller.js index 208c4a3b8d..d3aec68845 100644 --- a/awx/ui/client/src/notifications/notification-templates-list/list.controller.js +++ b/awx/ui/client/src/notifications/notification-templates-list/list.controller.js @@ -7,10 +7,12 @@ export default ['$rootScope', '$scope', 'Wait', 'generateList', 'NotificationTemplatesList', 'GetBasePath', 'Rest', 'ProcessErrors', 'Prompt', '$state', 'GetChoices', 'Empty', 'Find', 'ngToast', '$compile', '$filter', 'Dataset', 'rbacUiControlService', + 'i18n', function( $rootScope, $scope, Wait, GenerateList, NotificationTemplatesList, GetBasePath, Rest, ProcessErrors, Prompt, $state, GetChoices, - Empty, Find, ngToast, $compile, $filter, Dataset, rbacUiControlService) { + Empty, Find, ngToast, $compile, $filter, Dataset, rbacUiControlService, + i18n) { var defaultUrl = GetBasePath('notification_templates'), list = NotificationTemplatesList; @@ -68,8 +70,8 @@ html = "\n"; html += "\n"; html += ""; - html += ""; - html += ""; + html += ""; + html += ""; html += "\n"; html += "\n"; html += "\n"; @@ -83,7 +85,7 @@ html += "\n"; html += "
StatusTime" + i18n._("Status") + "" + i18n._("Time") + "
\n"; } else { - html = "

No recent notifications.

\n"; + html = "

" + i18n._("No recent notifications.") + "

\n"; } notification_template.template_status_html = html; } @@ -110,7 +112,7 @@ }) .catch(function() { ngToast.danger({ - content: ` ${name}: Notification Failed.`, + content: ` ${name}: ` + i18n._('Notification Failed.'), }); }); @@ -179,12 +181,12 @@ }); }); }; - var bodyHtml = '
Are you sure you want to delete the notification template below?
' + $filter('sanitize')(name) + '
'; + var bodyHtml = '
' + i18n._('Are you sure you want to delete the notification template below?') + '
' + $filter('sanitize')(name) + '
'; Prompt({ - hdr: 'Delete', + hdr: i18n._('Delete'), body: bodyHtml, action: action, - actionText: 'DELETE' + actionText: i18n._('DELETE') }); }; } diff --git a/awx/ui/client/src/notifications/notificationTemplates.form.js b/awx/ui/client/src/notifications/notificationTemplates.form.js index e115caf709..66e9a9854e 100644 --- a/awx/ui/client/src/notifications/notificationTemplates.form.js +++ b/awx/ui/client/src/notifications/notificationTemplates.form.js @@ -16,6 +16,9 @@ export default ['i18n', function(i18n) { addTitle: i18n._('New Notification Template'), editTitle: '{{ name }}', name: 'notification_template', + // I18N for "CREATE NOTIFICATION_TEMPLATE" + // on /#/notification_templates/add + breadcrumbName: i18n._('NOTIFICATION TEMPLATE'), stateTree: 'notifications', basePath: 'notification_templates', showActions: true, @@ -389,19 +392,19 @@ export default ['i18n', function(i18n) { ngDisabled: '!(notification_template.summary_fields.user_capabilities.edit || canAdd)' }, email_options: { - label: 'Options', + label: i18n._('Options'), type: 'radio_group', subForm: 'typeSubForm', ngShow: "notification_type.value == 'email'", ngChange: "emailOptionsChange()", options: [{ value: 'use_tls', - label: 'Use TLS', + label: i18n._('Use TLS'), ngShow: "notification_type.value == 'email' ", labelClass: 'NotificationsForm-radioButtons' }, { value: 'use_ssl', - label: 'Use SSL', + label: i18n._('Use SSL'), ngShow: "notification_type.value == 'email'", labelClass: 'NotificationsForm-radioButtons' }] diff --git a/awx/ui/client/src/notifications/notificationTemplates.list.js b/awx/ui/client/src/notifications/notificationTemplates.list.js index 92324c8701..70b9e21921 100644 --- a/awx/ui/client/src/notifications/notificationTemplates.list.js +++ b/awx/ui/client/src/notifications/notificationTemplates.list.js @@ -23,7 +23,7 @@ export default ['i18n', function(i18n){ nosort: true, icon: 'icon-job-{{ notification_template.status }}', awPopOver: '{{ notification_template.template_status_html }}', - dataTitle: "Recent Notifications", + dataTitle: i18n._("Recent Notifications"), dataPlacement: 'right', columnClass: 'col-lg-1 col-md-1 col-sm-2 col-xs-2 List-staticColumn--smallStatus' }, diff --git a/awx/ui/client/src/organizations/list/organizations-list.controller.js b/awx/ui/client/src/organizations/list/organizations-list.controller.js index 3849c123a4..a718db2548 100644 --- a/awx/ui/client/src/organizations/list/organizations-list.controller.js +++ b/awx/ui/client/src/organizations/list/organizations-list.controller.js @@ -7,10 +7,10 @@ export default ['$stateParams', '$scope', '$rootScope', '$location', '$log', '$compile', 'Rest', 'OrganizationList', 'Alert', 'Prompt', 'ClearScope', - 'ProcessErrors', 'GetBasePath', 'Wait', '$state', 'rbacUiControlService', '$filter', 'Dataset', + 'ProcessErrors', 'GetBasePath', 'Wait', '$state', 'rbacUiControlService', '$filter', 'Dataset', 'i18n', function($stateParams, $scope, $rootScope, $location, $log, $compile, Rest, OrganizationList, Alert, Prompt, ClearScope, - ProcessErrors, GetBasePath, Wait, $state, rbacUiControlService, $filter, Dataset) { + ProcessErrors, GetBasePath, Wait, $state, rbacUiControlService, $filter, Dataset, i18n) { ClearScope(); @@ -162,10 +162,10 @@ export default ['$stateParams', '$scope', '$rootScope', '$location', }; Prompt({ - hdr: 'Delete', - body: '
Are you sure you want to delete the organization below?
' + $filter('sanitize')(name) + '
', + hdr: i18n._('Delete'), + body: '
' + i18n._('Are you sure you want to delete the organization below?') + '
' + $filter('sanitize')(name) + '
', action: action, - actionText: 'DELETE' + actionText: i18n._('DELETE') }); }; } diff --git a/awx/ui/client/src/partials/logviewer.html b/awx/ui/client/src/partials/logviewer.html index 59fe6ca045..e3c188010b 100644 --- a/awx/ui/client/src/partials/logviewer.html +++ b/awx/ui/client/src/partials/logviewer.html @@ -1,12 +1,12 @@