From 6b418d4a5cc07e2a95e0a5be08b8645c37cc8855 Mon Sep 17 00:00:00 2001 From: Chris Houseknecht Date: Tue, 19 Nov 2013 08:17:08 +0000 Subject: [PATCH] Added activity stream to all tabs and to the tab detail page. Enabled working breadcrumbs on the activity stream widget. Added round corners to th stream containers so it at least looks OK when the bottom occassionally overllapses footer. Adjusted the stream widget's default url to include detail object's primary key, limiting rows to the object being viewed. --- awx/ui/static/js/controllers/Credentials.js | 12 ++- awx/ui/static/js/controllers/Inventories.js | 13 ++- awx/ui/static/js/controllers/JobTemplates.js | 12 ++- awx/ui/static/js/controllers/Organizations.js | 6 +- awx/ui/static/js/controllers/Projects.js | 12 ++- awx/ui/static/js/controllers/Teams.js | 12 ++- awx/ui/static/js/controllers/Users.js | 12 ++- awx/ui/static/js/forms/Credentials.js | 14 ++- awx/ui/static/js/forms/Inventories.js | 12 +++ awx/ui/static/js/forms/JobTemplates.js | 12 +++ awx/ui/static/js/forms/Organizations.js | 12 +++ awx/ui/static/js/forms/Projects.js | 12 +++ awx/ui/static/js/forms/Teams.js | 12 +++ awx/ui/static/js/forms/Users.js | 18 ++-- awx/ui/static/js/lists/Credentials.js | 9 ++ awx/ui/static/js/lists/Inventories.js | 9 ++ awx/ui/static/js/lists/JobTemplates.js | 9 ++ awx/ui/static/js/lists/Projects.js | 9 ++ awx/ui/static/js/lists/Teams.js | 9 ++ awx/ui/static/js/lists/Users.js | 9 ++ awx/ui/static/js/widgets/Stream.js | 90 ++++++++++++++----- awx/ui/static/less/ansible-ui.less | 2 +- awx/ui/static/lib/ansible/form-generator.js | 33 ++++++- awx/ui/static/lib/ansible/list-generator.js | 13 ++- 24 files changed, 304 insertions(+), 59 deletions(-) diff --git a/awx/ui/static/js/controllers/Credentials.js b/awx/ui/static/js/controllers/Credentials.js index b4b9c2d976..e9e8213236 100644 --- a/awx/ui/static/js/controllers/Credentials.js +++ b/awx/ui/static/js/controllers/Credentials.js @@ -12,7 +12,7 @@ function CredentialsList ($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, CredentialList, GenerateList, LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, ReturnToCaller, - ClearScope, ProcessErrors, GetBasePath, SelectionInit, GetChoices, Wait) + ClearScope, ProcessErrors, GetBasePath, SelectionInit, GetChoices, Wait, Stream) { ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior //scope. @@ -74,6 +74,8 @@ function CredentialsList ($scope, $rootScope, $location, $log, $routeParams, Res LoadBreadCrumbs(); + + scope.showActivity = function() { Stream(); } scope.addCredential = function() { $location.path($location.path() + '/add'); @@ -112,7 +114,7 @@ function CredentialsList ($scope, $rootScope, $location, $log, $routeParams, Res CredentialsList.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'CredentialList', 'GenerateList', 'LoadBreadCrumbs', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope', 'ProcessErrors', - 'GetBasePath', 'SelectionInit', 'GetChoices', 'Wait' ]; + 'GetBasePath', 'SelectionInit', 'GetChoices', 'Wait', 'Stream' ]; function CredentialsAdd ($scope, $rootScope, $compile, $location, $log, $routeParams, CredentialForm, @@ -249,7 +251,7 @@ CredentialsAdd.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$lo function CredentialsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, CredentialForm, GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit, RelatedPaginateInit, ReturnToCaller, ClearScope, Prompt, GetBasePath, GetChoices, - KindChange, UserList, TeamList, LookUpInit, Empty, OwnerChange, FormSave + KindChange, UserList, TeamList, LookUpInit, Empty, OwnerChange, FormSave, Stream ) { ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior @@ -390,6 +392,8 @@ function CredentialsEdit ($scope, $rootScope, $compile, $location, $log, $routeP variable: 'credential_kind_options', callback: 'choicesReady' }); + + scope.showActivity = function() { Stream(); } // Save changes to the parent scope.formSave = function() { generator.clearApiErrors(); FormSave({ scope: scope, mode: 'edit' }) }; @@ -488,5 +492,5 @@ function CredentialsEdit ($scope, $rootScope, $compile, $location, $log, $routeP CredentialsEdit.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'CredentialForm', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit', 'RelatedPaginateInit', 'ReturnToCaller', 'ClearScope', 'Prompt', 'GetBasePath', 'GetChoices', - 'KindChange', 'UserList', 'TeamList', 'LookUpInit', 'Empty', 'OwnerChange', 'FormSave']; + 'KindChange', 'UserList', 'TeamList', 'LookUpInit', 'Empty', 'OwnerChange', 'FormSave', 'Stream']; diff --git a/awx/ui/static/js/controllers/Inventories.js b/awx/ui/static/js/controllers/Inventories.js index 49f441dd03..a2457ae4ec 100644 --- a/awx/ui/static/js/controllers/Inventories.js +++ b/awx/ui/static/js/controllers/Inventories.js @@ -12,7 +12,7 @@ function InventoriesList ($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, InventoryList, GenerateList, LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, ReturnToCaller, - ClearScope, ProcessErrors, GetBasePath, Wait) + ClearScope, ProcessErrors, GetBasePath, Wait, Stream) { ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior //scope. @@ -138,6 +138,8 @@ function InventoriesList ($scope, $rootScope, $location, $log, $routeParams, Res } }); + + scope.showActivity = function() { Stream(); } scope.addInventory = function() { $location.path($location.path() + '/add'); @@ -202,7 +204,7 @@ function InventoriesList ($scope, $rootScope, $location, $log, $routeParams, Res InventoriesList.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'InventoryList', 'GenerateList', 'LoadBreadCrumbs', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope', 'ProcessErrors', - 'GetBasePath', 'Wait' ]; + 'GetBasePath', 'Wait', 'Stream' ]; function InventoriesAdd ($scope, $rootScope, $compile, $location, $log, $routeParams, InventoryForm, @@ -317,7 +319,8 @@ InventoriesAdd.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$lo function InventoriesEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, InventoryForm, GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit, RelatedPaginateInit, ReturnToCaller, ClearScope, LookUpInit, Prompt, OrganizationList, - GetBasePath, LoadInventory, ParseTypeChange, EditInventory, SaveInventory, PostLoadInventory + GetBasePath, LoadInventory, ParseTypeChange, EditInventory, SaveInventory, PostLoadInventory, + Stream ) { ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior @@ -346,6 +349,8 @@ function InventoriesEdit ($scope, $rootScope, $compile, $location, $log, $routeP LoadInventory({ scope: scope, doPostSteps: false }); + scope.showActivity = function() { Stream(); } + // Cancel scope.formReset = function() { generator.reset(); @@ -372,6 +377,6 @@ InventoriesEdit.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$l 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit', 'RelatedPaginateInit', 'ReturnToCaller', 'ClearScope', 'LookUpInit', 'Prompt', 'OrganizationList', 'GetBasePath', 'LoadInventory', 'ParseTypeChange', 'EditInventory', - 'SaveInventory', 'PostLoadInventory' + 'SaveInventory', 'PostLoadInventory', 'Stream' ]; diff --git a/awx/ui/static/js/controllers/JobTemplates.js b/awx/ui/static/js/controllers/JobTemplates.js index 287049fe85..31a2a8aeeb 100644 --- a/awx/ui/static/js/controllers/JobTemplates.js +++ b/awx/ui/static/js/controllers/JobTemplates.js @@ -13,7 +13,7 @@ function JobTemplatesList ($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, JobTemplateList, GenerateList, LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, ReturnToCaller, ClearScope, ProcessErrors, GetBasePath, PromptPasswords, JobTemplateForm, CredentialList, - LookUpInit, SubmitJob, Wait) + LookUpInit, SubmitJob, Wait, Stream) { ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior //scope. @@ -39,6 +39,8 @@ function JobTemplatesList ($scope, $rootScope, $location, $log, $routeParams, Re LoadBreadCrumbs(); + scope.showActivity = function() { Stream(); } + scope.addJobTemplate = function() { $location.path($location.path() + '/add'); } @@ -80,7 +82,7 @@ function JobTemplatesList ($scope, $rootScope, $location, $log, $routeParams, Re JobTemplatesList.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'JobTemplateList', 'GenerateList', 'LoadBreadCrumbs', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope', 'ProcessErrors','GetBasePath', 'PromptPasswords', 'JobTemplateForm', 'CredentialList', 'LookUpInit', - 'SubmitJob', 'Wait' + 'SubmitJob', 'Wait', 'Stream' ]; function JobTemplatesAdd ($scope, $rootScope, $compile, $location, $log, $routeParams, JobTemplateForm, @@ -300,7 +302,7 @@ function JobTemplatesEdit ($scope, $rootScope, $compile, $location, $log, $route GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit, RelatedPaginateInit, ReturnToCaller, ClearScope, InventoryList, CredentialList, ProjectList, LookUpInit, PromptPasswords, GetBasePath, md5Setup, ParseTypeChange, - JobStatusToolTip, FormatDate, Wait) + JobStatusToolTip, FormatDate, Wait, Stream) { ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior //scope. @@ -598,6 +600,8 @@ function JobTemplatesEdit ($scope, $rootScope, $compile, $location, $log, $route } }; + scope.showActivity = function() { Stream(); } + // Cancel scope.formReset = function() { generator.reset(); @@ -651,5 +655,5 @@ JobTemplatesEdit.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$ 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit', 'RelatedPaginateInit', 'ReturnToCaller', 'ClearScope', 'InventoryList', 'CredentialList', 'ProjectList', 'LookUpInit', 'PromptPasswords', 'GetBasePath', 'md5Setup', 'ParseTypeChange', - 'JobStatusToolTip', 'FormatDate', 'Wait' + 'JobStatusToolTip', 'FormatDate', 'Wait', 'Stream' ]; diff --git a/awx/ui/static/js/controllers/Organizations.js b/awx/ui/static/js/controllers/Organizations.js index 743311cc8f..f9df59c865 100644 --- a/awx/ui/static/js/controllers/Organizations.js +++ b/awx/ui/static/js/controllers/Organizations.js @@ -134,7 +134,7 @@ OrganizationsAdd.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$ function OrganizationsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, OrganizationForm, GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit, - RelatedPaginateInit, Prompt, ClearScope, GetBasePath, Wait) + RelatedPaginateInit, Prompt, ClearScope, GetBasePath, Wait, Stream) { ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior //scope. @@ -211,6 +211,8 @@ function OrganizationsEdit ($scope, $rootScope, $compile, $location, $log, $rout }); }; + scope.showActivity = function() { Stream(); } + // Reset the form scope.formReset = function() { $rootScope.flashMessage = null; @@ -264,4 +266,4 @@ function OrganizationsEdit ($scope, $rootScope, $compile, $location, $log, $rout OrganizationsEdit.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'OrganizationForm', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit', - 'RelatedPaginateInit', 'Prompt', 'ClearScope', 'GetBasePath', 'Wait']; + 'RelatedPaginateInit', 'Prompt', 'ClearScope', 'GetBasePath', 'Wait', 'Stream']; diff --git a/awx/ui/static/js/controllers/Projects.js b/awx/ui/static/js/controllers/Projects.js index 3b610d4a3e..db71fa08e5 100644 --- a/awx/ui/static/js/controllers/Projects.js +++ b/awx/ui/static/js/controllers/Projects.js @@ -13,7 +13,7 @@ function ProjectsList ($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, ProjectList, GenerateList, LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, ReturnToCaller, ClearScope, ProcessErrors, GetBasePath, SelectionInit, ProjectUpdate, ProjectStatus, - FormatDate, Refresh, Wait) + FormatDate, Refresh, Wait, Stream) { ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior //scope. @@ -67,6 +67,8 @@ function ProjectsList ($scope, $rootScope, $location, $log, $routeParams, Rest, scope.search(list.iterator); LoadBreadCrumbs(); + + scope.showActivity = function() { Stream(); } scope.addProject = function() { $location.path($location.path() + '/add'); @@ -228,7 +230,7 @@ function ProjectsList ($scope, $rootScope, $location, $log, $routeParams, Rest, ProjectsList.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'ProjectList', 'GenerateList', 'LoadBreadCrumbs', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope', 'ProcessErrors', - 'GetBasePath', 'SelectionInit', 'ProjectUpdate', 'ProjectStatus', 'FormatDate', 'Refresh', 'Wait' ]; + 'GetBasePath', 'SelectionInit', 'ProjectUpdate', 'ProjectStatus', 'FormatDate', 'Refresh', 'Wait', 'Stream']; function ProjectsAdd ($scope, $rootScope, $compile, $location, $log, $routeParams, ProjectsForm, @@ -370,7 +372,7 @@ ProjectsAdd.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', function ProjectsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, ProjectsForm, GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit, RelatedPaginateInit, Prompt, ClearScope, GetBasePath, ReturnToCaller, GetProjectPath, - Authorization, CredentialList, LookUpInit, GetChoices, Empty, DebugForm, Wait) + Authorization, CredentialList, LookUpInit, GetChoices, Empty, DebugForm, Wait, Stream) { ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior //scope. @@ -542,6 +544,8 @@ function ProjectsEdit ($scope, $rootScope, $compile, $location, $log, $routePara }); }; + scope.showActivity = function() { Stream(); } + // Related set: Add button scope.add = function(set) { $rootScope.flashMessage = null; @@ -600,5 +604,5 @@ ProjectsEdit.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log' 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit', 'RelatedPaginateInit', 'Prompt', 'ClearScope', 'GetBasePath', 'ReturnToCaller', 'GetProjectPath', 'Authorization', 'CredentialList', 'LookUpInit', 'GetChoices', 'Empty', - 'DebugForm', 'Wait' + 'DebugForm', 'Wait', 'Stream' ]; diff --git a/awx/ui/static/js/controllers/Teams.js b/awx/ui/static/js/controllers/Teams.js index 9d5279f145..c310086879 100644 --- a/awx/ui/static/js/controllers/Teams.js +++ b/awx/ui/static/js/controllers/Teams.js @@ -12,7 +12,7 @@ function TeamsList ($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, TeamList, GenerateList, LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, ReturnToCaller, - ClearScope, ProcessErrors, SetTeamListeners, GetBasePath, SelectionInit, Wait) + ClearScope, ProcessErrors, SetTeamListeners, GetBasePath, SelectionInit, Wait, Stream) { ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior //scope. @@ -44,6 +44,8 @@ function TeamsList ($scope, $rootScope, $location, $log, $routeParams, Rest, Ale scope.search(list.iterator); LoadBreadCrumbs(); + + scope.showActivity = function() { Stream(); } scope.addTeam = function() { $location.path($location.path() + '/add'); @@ -93,7 +95,7 @@ function TeamsList ($scope, $rootScope, $location, $log, $routeParams, Rest, Ale TeamsList.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'TeamList', 'GenerateList', 'LoadBreadCrumbs', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope', 'ProcessErrors', - 'SetTeamListeners', 'GetBasePath', 'SelectionInit', 'Wait']; + 'SetTeamListeners', 'GetBasePath', 'SelectionInit', 'Wait', 'Stream' ]; function TeamsAdd ($scope, $rootScope, $compile, $location, $log, $routeParams, TeamForm, GenerateForm, @@ -157,7 +159,7 @@ TeamsAdd.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$ function TeamsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, TeamForm, GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit, RelatedPaginateInit, ReturnToCaller, ClearScope, LookUpInit, Prompt, - GetBasePath, CheckAccess, OrganizationList, Wait) + GetBasePath, CheckAccess, OrganizationList, Wait, Stream) { ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior //scope. @@ -233,6 +235,8 @@ function TeamsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, { hdr: 'Error!', msg: 'Failed to retrieve team: ' + $routeParams.team_id + '. GET status: ' + status }); }); + scope.showActivity = function() { Stream(); } + // Save changes to the parent scope.formSave = function() { generator.clearApiErrors(); @@ -344,6 +348,6 @@ function TeamsEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, TeamsEdit.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'TeamForm', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit', 'RelatedPaginateInit', 'ReturnToCaller', 'ClearScope', 'LookUpInit', 'Prompt', - 'GetBasePath', 'CheckAccess', 'OrganizationList', 'Wait' + 'GetBasePath', 'CheckAccess', 'OrganizationList', 'Wait', 'Stream' ]; diff --git a/awx/ui/static/js/controllers/Users.js b/awx/ui/static/js/controllers/Users.js index a0e73ce685..0df6654ab3 100644 --- a/awx/ui/static/js/controllers/Users.js +++ b/awx/ui/static/js/controllers/Users.js @@ -12,7 +12,7 @@ function UsersList ($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, UserList, GenerateList, LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, ReturnToCaller, - ClearScope, ProcessErrors, GetBasePath, SelectionInit, Wait) + ClearScope, ProcessErrors, GetBasePath, SelectionInit, Wait, Stream) { ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior //scope. @@ -35,6 +35,8 @@ function UsersList ($scope, $rootScope, $location, $log, $routeParams, Rest, Ale var url = (base == 'organizations') ? GetBasePath('organizations') + $routeParams.organization_id + '/users/' : GetBasePath('teams') + $routeParams.team_id + '/users/'; SelectionInit({ scope: scope, list: list, url: url, returnToCaller: 1 }); + + scope.showActivity = function() { Stream(); } scope.addUser = function() { $location.path($location.path() + '/add'); @@ -73,7 +75,7 @@ function UsersList ($scope, $rootScope, $location, $log, $routeParams, Rest, Ale UsersList.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'UserList', 'GenerateList', 'LoadBreadCrumbs', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope', 'ProcessErrors', - 'GetBasePath', 'SelectionInit', 'Wait' ]; + 'GetBasePath', 'SelectionInit', 'Wait', 'Stream']; function UsersAdd ($scope, $rootScope, $compile, $location, $log, $routeParams, UserForm, @@ -186,7 +188,7 @@ UsersAdd.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$ function UsersEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, UserForm, GenerateForm, Rest, Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit, RelatedPaginateInit, ReturnToCaller, ClearScope, GetBasePath, Prompt, CheckAccess, - ResetForm, Wait) + ResetForm, Wait, Stream) { ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior //scope. @@ -288,6 +290,8 @@ function UsersEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, }); }; + scope.showActivity = function() { Stream(); } + // Cancel scope.formReset = function() { $rootScope.flashMessage = null; @@ -435,5 +439,5 @@ function UsersEdit ($scope, $rootScope, $compile, $location, $log, $routeParams, UsersEdit.$inject = [ '$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'UserForm', 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit', 'RelatedPaginateInit', 'ReturnToCaller', 'ClearScope', 'GetBasePath', 'Prompt', 'CheckAccess', - 'ResetForm', 'Wait' ]; + 'ResetForm', 'Wait', 'Stream']; diff --git a/awx/ui/static/js/forms/Credentials.js b/awx/ui/static/js/forms/Credentials.js index 495a557e17..88dd7b4c24 100644 --- a/awx/ui/static/js/forms/Credentials.js +++ b/awx/ui/static/js/forms/Credentials.js @@ -14,7 +14,19 @@ angular.module('CredentialFormDefinition', []) name: 'credential', well: true, forceListeners: true, - + + actions: { + stream: { + 'class': "btn-primary btn-xs activity-btn", + ngClick: "showActivity()", + awToolTip: "View Activity Stream", + dataPlacement: "top", + icon: "icon-comments-alt", + mode: 'edit', + iconSize: 'large' + } + }, + fields: { name: { label: 'Name', diff --git a/awx/ui/static/js/forms/Inventories.js b/awx/ui/static/js/forms/Inventories.js index dacf6a9b14..4aad1016ed 100644 --- a/awx/ui/static/js/forms/Inventories.js +++ b/awx/ui/static/js/forms/Inventories.js @@ -15,6 +15,18 @@ angular.module('InventoryFormDefinition', []) name: 'inventory', parseTypeName: 'inventoryParseType', well: true, + + actions: { + stream: { + 'class': "btn-primary btn-xs activity-btn", + ngClick: "showActivity()", + awToolTip: "View Activity Stream", + dataPlacement: "top", + icon: "icon-comments-alt", + mode: 'edit', + iconSize: 'large' + } + }, navigationLinks: { inventory: { diff --git a/awx/ui/static/js/forms/JobTemplates.js b/awx/ui/static/js/forms/JobTemplates.js index 788883c1ba..6966482b47 100644 --- a/awx/ui/static/js/forms/JobTemplates.js +++ b/awx/ui/static/js/forms/JobTemplates.js @@ -16,6 +16,18 @@ angular.module('JobTemplateFormDefinition', []) twoColumns: true, well: true, + actions: { + stream: { + 'class': "btn-primary btn-xs activity-btn", + ngClick: "showActivity()", + awToolTip: "View Activity Stream", + dataPlacement: "top", + icon: "icon-comments-alt", + mode: 'edit', + iconSize: 'large' + } + }, + fields: { name: { label: 'Name', diff --git a/awx/ui/static/js/forms/Organizations.js b/awx/ui/static/js/forms/Organizations.js index 1cc07304cc..cad661817c 100644 --- a/awx/ui/static/js/forms/Organizations.js +++ b/awx/ui/static/js/forms/Organizations.js @@ -15,6 +15,18 @@ angular.module('OrganizationFormDefinition', []) name: 'organization', //entity or model name in singular form well: true, //Wrap the form with TB well/ + actions: { + stream: { + 'class': "btn-primary btn-xs activity-btn", + ngClick: "showActivity()", + awToolTip: "View Activity Stream", + dataPlacement: "top", + icon: "icon-comments-alt", + mode: 'edit', + iconSize: 'large' + } + }, + fields: { name: { label: 'Name', diff --git a/awx/ui/static/js/forms/Projects.js b/awx/ui/static/js/forms/Projects.js index 778f7d30ad..c38477326c 100644 --- a/awx/ui/static/js/forms/Projects.js +++ b/awx/ui/static/js/forms/Projects.js @@ -17,6 +17,18 @@ angular.module('ProjectFormDefinition', []) well: true, // Wrap the form with TB well forceListeners: true, + actions: { + stream: { + 'class': "btn-primary btn-xs activity-btn", + ngClick: "showActivity()", + awToolTip: "View Activity Stream", + dataPlacement: "top", + icon: "icon-comments-alt", + mode: 'edit', + iconSize: 'large' + } + }, + fields: { name: { label: 'Name', diff --git a/awx/ui/static/js/forms/Teams.js b/awx/ui/static/js/forms/Teams.js index 17056307ab..e93d4f4a87 100644 --- a/awx/ui/static/js/forms/Teams.js +++ b/awx/ui/static/js/forms/Teams.js @@ -19,6 +19,18 @@ angular.module('TeamFormDefinition', []) collapseMode: 'edit', collapseOpen: true, + actions: { + stream: { + 'class': "btn-primary btn-xs activity-btn", + ngClick: "showActivity()", + awToolTip: "View Activity Stream", + dataPlacement: "top", + icon: "icon-comments-alt", + mode: 'edit', + iconSize: 'large' + } + }, + fields: { name: { label: 'Name', diff --git a/awx/ui/static/js/forms/Users.js b/awx/ui/static/js/forms/Users.js index ec04dd9326..7d497de179 100644 --- a/awx/ui/static/js/forms/Users.js +++ b/awx/ui/static/js/forms/Users.js @@ -13,13 +13,21 @@ angular.module('UserFormDefinition', []) addTitle: 'Create User', //Legend in add mode editTitle: '{{ username }}', //Legend in edit mode name: 'user', //Form name attribute - well: true, //Wrap the form with TB well - /*collapse: true, - collapseTitle: 'User Settings', - collapseMode: 'edit', - collapseOpen: true,*/ + well: true, //Wrap the form with TB well forceListeners: true, + actions: { + stream: { + 'class': "btn-primary btn-xs activity-btn", + ngClick: "showActivity()", + awToolTip: "View Activity Stream", + dataPlacement: "top", + icon: "icon-comments-alt", + mode: 'edit', + iconSize: 'large' + } + }, + fields: { first_name: { label: 'First Name', diff --git a/awx/ui/static/js/lists/Credentials.js b/awx/ui/static/js/lists/Credentials.js index 004b2be5b5..1f525c15c4 100644 --- a/awx/ui/static/js/lists/Credentials.js +++ b/awx/ui/static/js/lists/Credentials.js @@ -61,6 +61,15 @@ angular.module('CredentialsListDefinition', []) ngClick: 'addCredential()', "class": 'btn-success btn-xs', awToolTip: 'Create a new credential' + }, + stream: { + 'class': "btn-primary btn-xs activity-btn", + ngClick: "showActivity()", + awToolTip: "View Activity Stream", + dataPlacement: "top", + icon: "icon-comments-alt", + mode: 'all', + iconSize: 'large' } }, diff --git a/awx/ui/static/js/lists/Inventories.js b/awx/ui/static/js/lists/Inventories.js index f81b785a16..1957445812 100644 --- a/awx/ui/static/js/lists/Inventories.js +++ b/awx/ui/static/js/lists/Inventories.js @@ -88,6 +88,15 @@ angular.module('InventoriesListDefinition', []) ngClick: 'addInventory()', "class": 'btn-xs btn-success', awToolTip: 'Create a new inventory' + }, + stream: { + 'class': "btn-primary btn-xs activity-btn", + ngClick: "showActivity()", + awToolTip: "View Activity Stream", + dataPlacement: "top", + icon: "icon-comments-alt", + mode: 'all', + iconSize: 'large' } }, diff --git a/awx/ui/static/js/lists/JobTemplates.js b/awx/ui/static/js/lists/JobTemplates.js index 05e1ba5e40..18da0b8f76 100644 --- a/awx/ui/static/js/lists/JobTemplates.js +++ b/awx/ui/static/js/lists/JobTemplates.js @@ -37,6 +37,15 @@ angular.module('JobTemplatesListDefinition', []) "class": 'btn-success btn-xs', basePaths: ['job_templates'], awToolTip: 'Create a new template' + }, + stream: { + 'class': "btn-primary btn-xs activity-btn", + ngClick: "showActivity()", + awToolTip: "View Activity Stream", + dataPlacement: "top", + icon: "icon-comments-alt", + mode: 'all', + iconSize: 'large' } }, diff --git a/awx/ui/static/js/lists/Projects.js b/awx/ui/static/js/lists/Projects.js index 64d37e6c91..4add716f30 100644 --- a/awx/ui/static/js/lists/Projects.js +++ b/awx/ui/static/js/lists/Projects.js @@ -80,6 +80,15 @@ angular.module('ProjectsListDefinition', []) awToolTip: "Refresh the page", ngClick: "refresh()", iconSize: 'large' + }, + stream: { + 'class': "btn-primary btn-xs activity-btn", + ngClick: "showActivity()", + awToolTip: "View Activity Stream", + dataPlacement: "top", + icon: "icon-comments-alt", + mode: 'all', + iconSize: 'large' } }, diff --git a/awx/ui/static/js/lists/Teams.js b/awx/ui/static/js/lists/Teams.js index 02b1b332ab..0b08d352bf 100644 --- a/awx/ui/static/js/lists/Teams.js +++ b/awx/ui/static/js/lists/Teams.js @@ -42,6 +42,15 @@ angular.module('TeamsListDefinition', []) ngClick: 'addTeam()', "class": 'btn-xs btn-success', awToolTip: 'Create a new team' + }, + stream: { + 'class': "btn-primary btn-xs activity-btn", + ngClick: "showActivity()", + awToolTip: "View Activity Stream", + dataPlacement: "top", + icon: "icon-comments-alt", + mode: 'all', + iconSize: 'large' } }, diff --git a/awx/ui/static/js/lists/Users.js b/awx/ui/static/js/lists/Users.js index 1a37785318..7a398c3245 100644 --- a/awx/ui/static/js/lists/Users.js +++ b/awx/ui/static/js/lists/Users.js @@ -42,6 +42,15 @@ angular.module('UserListDefinition', []) basePaths: ['organizations','users'], // base path must be in list, or action not available "class": 'btn-success btn-xs', awToolTip: 'Create a new user' + }, + stream: { + 'class': "btn-primary btn-xs activity-btn", + ngClick: "showActivity()", + awToolTip: "View Activity Stream", + dataPlacement: "top", + icon: "icon-comments-alt", + mode: 'all', + iconSize: 'large' } }, diff --git a/awx/ui/static/js/widgets/Stream.js b/awx/ui/static/js/widgets/Stream.js index 433a5a0879..bfc15e8858 100644 --- a/awx/ui/static/js/widgets/Stream.js +++ b/awx/ui/static/js/widgets/Stream.js @@ -35,7 +35,7 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti } }]) - .factory('HideStream', [ function() { + .factory('HideStream', [ 'LoadBreadCrumbs', function(LoadBreadCrumbs) { return function() { // Remove the stream widget @@ -52,6 +52,52 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti stream.unbind(); $('#tab-content-container').css({ 'min-height': 0 }); //let the parent height go back to normal }, 500); + + LoadBreadCrumbs(); + } + }]) + + .factory('StreamBreadCrumbs', ['$rootScope', '$location', function($rootScope, $location) { + return function() { + // Load the breadcrumbs array. We have to do things a bit different than our standing Utilities.LoadBreadcrumbs. + // Rather than botch that all up, we'll do our own thing here. + $rootScope.breadcrumbs = []; + var paths = $location.path().split('/'); + paths.splice(0,1); + var path, title; + for (var i=0; i < paths.length; i++) { + if (/^\d+/.test(paths[i])) { + path=''; + title=''; + for (j=0; j <= i; j++) { + path += '/' + paths[j]; + } + for (j=0; j < $rootScope.crumbCache.length; j++) { + if ($rootScope.crumbCache[j].path == path) { + title = $rootScope.crumbCache[j].title; + break; + } + } + if (!title) { + title = paths[i - 1].substr(0,paths[i - 1].length - 1); + title = (title == 'inventorie') ? 'inventory' : title; + } + } + else { + path=''; + title=''; + if (i > 0) { + for (j=0; j <= i; j++) { + path += '/' + paths[j]; + } + } + else { + path = '/' + paths[i]; + } + title = paths[i]; + } + $rootScope.breadcrumbs.push({ path: path, title: title, ngClick: "closeStream('" + path + "')" }); + } } }]) @@ -184,9 +230,9 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti .factory('Stream', ['$rootScope', '$location', 'Rest', 'GetBasePath', 'ProcessErrors', 'Wait', 'StreamList', 'SearchInit', 'PaginateInit', 'GenerateList', 'FormatDate', 'ShowStream', 'HideStream', 'BuildDescription', 'FixUrl', 'BuildUrl', - 'ShowDetail', 'LoadBreadCrumbs', + 'ShowDetail', 'StreamBreadCrumbs', function($rootScope, $location, Rest, GetBasePath, ProcessErrors, Wait, StreamList, SearchInit, PaginateInit, GenerateList, - FormatDate, ShowStream, HideStream, BuildDescription, FixUrl, BuildUrl, ShowDetail, LoadBreadCrumbs) { + FormatDate, ShowStream, HideStream, BuildDescription, FixUrl, BuildUrl, ShowDetail, StreamBreadCrumbs) { return function(params) { var list = StreamList; @@ -195,22 +241,23 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti var base = $location.path().replace(/^\//,'').split('/')[0]; if (base !== 'home') { + // Restrict what we're looking at based on the path var type = (base == 'inventories') ? 'inventory' : base.replace(/s$/,''); - defaultUrl += '?or__object1=' + type + '&or__object2=' + type; + var paths = $location.path().split('/'); + paths.splice(0,1); + if (paths.length > 1 && /^\d+/.test(paths[1])) { + defaultUrl += '?object1=' + type + '&object1_id=' + paths[i]; + } + else { + defaultUrl += '?or__object1=' + type + '&or__object2=' + type; + } } - - // Push the current page onto browser histor. If user clicks back button, restore current page without - // stream widget - // window.history.pushState({}, "AnsibleWorks AWX", $location.path()); // Add a container for the stream widget $('#tab-content-container').append('
'); - + + StreamBreadCrumbs(); ShowStream(); - if ($rootScope.breadcrumbs.length == 0) { - var title = base.substr(0,1).toUpperCase() + base.substr(1); - $rootScope.breadcrumbs.push({ path: $location.path(), title: title}); - } // Generate the list var scope = view.inject(list, { @@ -218,11 +265,15 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti id: 'stream-content', breadCrumbs: true, searchSize: 'col-lg-3', - secondWidget: true + secondWidget: true, + activityStream: true }); - scope.closeStream = function() { + scope.closeStream = function(inUrl) { HideStream(); + if (inUrl) { + $location.path(inUrl); + } } scope.refreshStream = function() { @@ -288,15 +339,6 @@ angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefiniti SearchInit({ scope: scope, set: list.name, list: list, url: defaultUrl }); PaginateInit({ scope: scope, list: list, url: defaultUrl }); scope.search(list.iterator); - - /* - scope.$watch(list.iterator + 'SearchField', function(newVal, oldVal) { - console.log('newVal: ' + newVal); - html += "" - html += "\n"; - });*/ } }]); diff --git a/awx/ui/static/less/ansible-ui.less b/awx/ui/static/less/ansible-ui.less index 2746804dd4..6533381c7f 100644 --- a/awx/ui/static/less/ansible-ui.less +++ b/awx/ui/static/less/ansible-ui.less @@ -22,7 +22,6 @@ @info-color: #3a87ad; @tip-background: #0088CC; -/*rgb(58, 135, 173);*/ @tip-color: #fff; @@ -1378,6 +1377,7 @@ tr td button i { #stream-container { display: none; + border-radius: 8px; } #stream-content { diff --git a/awx/ui/static/lib/ansible/form-generator.js b/awx/ui/static/lib/ansible/form-generator.js index 604a43bb4d..0a0c4f9d2c 100644 --- a/awx/ui/static/lib/ansible/form-generator.js +++ b/awx/ui/static/lib/ansible/form-generator.js @@ -932,6 +932,33 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies', 'Utilities']) return html; }, + getActions: function(options) { + // Use to add things like Activity Stream to a detail page + html = "
\n"; + for (action in this.form.actions) { + if (this.form.actions[action].mode == 'all' || this.form.actions[action].mode == options.mode) { + html += this.button(this.form.actions[action], action); + } + } + html += "
\n"; + return html; + }, + + /* + activityCrumbs: function() { + // Breadcrumbs for activity stream widget + // Make the links clickable using ng-click function so we can first remove the stream widget. + html = ''; + html += "
\n"; + html += "\n
\n"; + return html; + }, + */ + breadCrumbs: function(options, navigation) { var html = ''; @@ -1010,7 +1037,7 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies', 'Utilities']) html += this.breadCrumbs(options); } } - + if ((!this.modal && this.form.statusFields)) { // Add status fields section (used in Jobs form) html += "
\n"; @@ -1058,6 +1085,10 @@ angular.module('FormGenerator', ['GeneratorHelpers', 'ngCookies', 'Utilities']) html += "
\n"; } + if (!this.modal && this.form.actions) { + html += this.getActions(options); + } + // Add a title and optionally a close button (used on Inventory->Groups) if ( (!options.modal) && this.form.showTitle ) { html += "
"; diff --git a/awx/ui/static/lib/ansible/list-generator.js b/awx/ui/static/lib/ansible/list-generator.js index 9fd9e667d7..27e6dae01c 100644 --- a/awx/ui/static/lib/ansible/list-generator.js +++ b/awx/ui/static/lib/ansible/list-generator.js @@ -112,7 +112,18 @@ angular.module('ListGenerator', ['GeneratorHelpers']) var html = ''; var list = this.list; - if (options.mode != 'lookup' && (options.breadCrumbs == undefined || options.breadCrumbs == true)) { + if (options.activityStream) { + // Breadcrumbs for activity stream widget + // Make the links clickable using ng-click function so we can first remove the stream widget + // before navigation + html += "
\n"; + html += "\n
\n"; + } + else if (options.mode != 'lookup' && (options.breadCrumbs == undefined || options.breadCrumbs == true)) { //Breadcrumbs html += "
\n"; html += "
    \n";