mirror of
https://github.com/ansible/awx.git
synced 2026-01-25 00:11:23 -03:30
First iteration of Activity Stream. Added Home/Groups page. Increased icon size for icon-only buttons. Dashboard jobs widget- group and job links now work. Closed AC-621, AC-618.
This commit is contained in:
parent
2c4d583f3e
commit
5a3977495a
618
awx/ui/static/html/event_log.html
Normal file
618
awx/ui/static/html/event_log.html
Normal file
@ -0,0 +1,618 @@
|
||||
{
|
||||
"count": 1,
|
||||
"next": null,
|
||||
"previous": null,
|
||||
"results": [
|
||||
{
|
||||
"id": 1,
|
||||
"url": "/api/v1/event_log/1/",
|
||||
"related": {
|
||||
"user": "/users/N/",
|
||||
"object1": "/organizations/N/",
|
||||
"object2": ""
|
||||
},
|
||||
|
||||
"summary_fields": {
|
||||
"object1": {
|
||||
"name": "Frito Lay",
|
||||
"description": "Salty Snacks"
|
||||
},
|
||||
"user": {
|
||||
"username": "chouseknecht"
|
||||
}
|
||||
},
|
||||
|
||||
"created": "2013-11-06T15:18:58.391Z",
|
||||
"modified": "2013-11-06T15:18:58.514Z",
|
||||
"user": 1,
|
||||
"event_time": "2013-11-06T15:18:58.514Z",
|
||||
"operation": "change",
|
||||
"changes": {
|
||||
"before": { "description": "Healthy Snacks" },
|
||||
"after": { "description": "Salty Snacks" }
|
||||
},
|
||||
"relationship": ""
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"url": "/api/v1/event_log/2/",
|
||||
"related": {
|
||||
"user": "/users/N/",
|
||||
"object1": "/groups/N/",
|
||||
"object2": ""
|
||||
},
|
||||
|
||||
"summary_fields": {
|
||||
"inventory": {
|
||||
"name": "Test Inventory",
|
||||
"description": "Testing activity stream"
|
||||
},
|
||||
"object1": {
|
||||
"name": "Group A",
|
||||
"description": "The A group"
|
||||
},
|
||||
"user": {
|
||||
"username": "chouseknecht"
|
||||
},
|
||||
"object2": {}
|
||||
},
|
||||
|
||||
"created": "2013-11-05T15:18:55.000Z",
|
||||
"modified": "2013-11-05T15:18:55.000Z",
|
||||
"user": 1,
|
||||
"event_time": "2013-11-06T15:18:55.000Z",
|
||||
"operation": "create",
|
||||
"changes": {
|
||||
"before": { "groups": [ "Group X", "Group Y", "Group Z" ] },
|
||||
"after": { "groups": [ "Group A", "Group X", "Group Y", "Group Z" ] }
|
||||
},
|
||||
"relationship": ""
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"url": "/api/v1/event_log/2/",
|
||||
"related": {
|
||||
"user": "/users/N/",
|
||||
"object1": "/groups/N/children",
|
||||
"object2": "/groups/N/"
|
||||
},
|
||||
|
||||
"summary_fields": {
|
||||
"inventory": {
|
||||
"name": "Test Inventory",
|
||||
"description": "Testing activity stream"
|
||||
},
|
||||
"object1": {
|
||||
"name": "Group A",
|
||||
"description": "The A group"
|
||||
},
|
||||
"user": { "username": "chouseknecht" },
|
||||
"object2": {
|
||||
"name": "Group B",
|
||||
"description": "The B group"
|
||||
}
|
||||
},
|
||||
|
||||
"created": "2013-11-05T15:18:58.391Z",
|
||||
"modified": "2013-11-05T15:18:58.514Z",
|
||||
"user": 1,
|
||||
"event_time": "2013-11-06T15:18:58.514Z",
|
||||
"operation": "associate",
|
||||
"changes": {
|
||||
"before": { "groups": [ "Group X", "Group Y", "Group Z" ] },
|
||||
"after": { "groups": [ "Group A", "Group X", "Group Y", "Group Z" ] }
|
||||
},
|
||||
"relationship": ""
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"url": "/api/v1/event_log/1/",
|
||||
"related": {
|
||||
"user": "/users/N/",
|
||||
"object1": "/organizations/N/",
|
||||
"object2": ""
|
||||
},
|
||||
|
||||
"summary_fields": {
|
||||
"object1": {
|
||||
"name": "Frito Lay",
|
||||
"description": "Salty Snacks"
|
||||
},
|
||||
"user": {
|
||||
"username": "chouseknecht"
|
||||
}
|
||||
},
|
||||
|
||||
"created": "2013-11-06T15:18:58.391Z",
|
||||
"modified": "2013-11-06T15:18:58.514Z",
|
||||
"user": 1,
|
||||
"event_time": "2013-11-06T15:18:58.514Z",
|
||||
"operation": "change",
|
||||
"changes": {
|
||||
"before": { "description": "Healthy Snacks" },
|
||||
"after": { "description": "Salty Snacks" }
|
||||
},
|
||||
"relationship": ""
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"url": "/api/v1/event_log/1/",
|
||||
"related": {
|
||||
"user": "/users/N/",
|
||||
"object1": "/organizations/N/",
|
||||
"object2": ""
|
||||
},
|
||||
|
||||
"summary_fields": {
|
||||
"object1": {
|
||||
"name": "Frito Lay",
|
||||
"description": "Salty Snacks"
|
||||
},
|
||||
"user": {
|
||||
"username": "chouseknecht"
|
||||
}
|
||||
},
|
||||
|
||||
"created": "2013-11-06T15:18:58.391Z",
|
||||
"modified": "2013-11-06T15:18:58.514Z",
|
||||
"user": 1,
|
||||
"event_time": "2013-11-06T15:18:58.514Z",
|
||||
"operation": "change",
|
||||
"changes": {
|
||||
"before": { "description": "Healthy Snacks" },
|
||||
"after": { "description": "Salty Snacks" }
|
||||
},
|
||||
"relationship": ""
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"url": "/api/v1/event_log/1/",
|
||||
"related": {
|
||||
"user": "/users/N/",
|
||||
"object1": "/organizations/N/",
|
||||
"object2": ""
|
||||
},
|
||||
|
||||
"summary_fields": {
|
||||
"object1": {
|
||||
"name": "Frito Lay",
|
||||
"description": "Salty Snacks"
|
||||
},
|
||||
"user": {
|
||||
"username": "chouseknecht"
|
||||
}
|
||||
},
|
||||
|
||||
"created": "2013-11-06T15:18:58.391Z",
|
||||
"modified": "2013-11-06T15:18:58.514Z",
|
||||
"user": 1,
|
||||
"event_time": "2013-11-06T15:18:58.514Z",
|
||||
"operation": "change",
|
||||
"changes": {
|
||||
"before": { "description": "Healthy Snacks" },
|
||||
"after": { "description": "Salty Snacks" }
|
||||
},
|
||||
"relationship": ""
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"url": "/api/v1/event_log/1/",
|
||||
"related": {
|
||||
"user": "/users/N/",
|
||||
"object1": "/organizations/N/",
|
||||
"object2": ""
|
||||
},
|
||||
|
||||
"summary_fields": {
|
||||
"object1": {
|
||||
"name": "Frito Lay",
|
||||
"description": "Salty Snacks"
|
||||
},
|
||||
"user": {
|
||||
"username": "chouseknecht"
|
||||
}
|
||||
},
|
||||
|
||||
"created": "2013-11-06T15:18:58.391Z",
|
||||
"modified": "2013-11-06T15:18:58.514Z",
|
||||
"user": 1,
|
||||
"event_time": "2013-11-06T15:18:58.514Z",
|
||||
"operation": "change",
|
||||
"changes": {
|
||||
"before": { "description": "Healthy Snacks" },
|
||||
"after": { "description": "Salty Snacks" }
|
||||
},
|
||||
"relationship": ""
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"url": "/api/v1/event_log/1/",
|
||||
"related": {
|
||||
"user": "/users/N/",
|
||||
"object1": "/organizations/N/",
|
||||
"object2": ""
|
||||
},
|
||||
|
||||
"summary_fields": {
|
||||
"object1": {
|
||||
"name": "Frito Lay",
|
||||
"description": "Salty Snacks"
|
||||
},
|
||||
"user": {
|
||||
"username": "chouseknecht"
|
||||
}
|
||||
},
|
||||
|
||||
"created": "2013-11-06T15:18:58.391Z",
|
||||
"modified": "2013-11-06T15:18:58.514Z",
|
||||
"user": 1,
|
||||
"event_time": "2013-11-06T15:18:58.514Z",
|
||||
"operation": "change",
|
||||
"changes": {
|
||||
"before": { "description": "Healthy Snacks" },
|
||||
"after": { "description": "Salty Snacks" }
|
||||
},
|
||||
"relationship": ""
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"url": "/api/v1/event_log/1/",
|
||||
"related": {
|
||||
"user": "/users/N/",
|
||||
"object1": "/organizations/N/",
|
||||
"object2": ""
|
||||
},
|
||||
|
||||
"summary_fields": {
|
||||
"object1": {
|
||||
"name": "Frito Lay",
|
||||
"description": "Salty Snacks"
|
||||
},
|
||||
"user": {
|
||||
"username": "chouseknecht"
|
||||
}
|
||||
},
|
||||
|
||||
"created": "2013-11-06T15:18:58.391Z",
|
||||
"modified": "2013-11-06T15:18:58.514Z",
|
||||
"user": 1,
|
||||
"event_time": "2013-11-06T15:18:58.514Z",
|
||||
"operation": "change",
|
||||
"changes": {
|
||||
"before": { "description": "Healthy Snacks" },
|
||||
"after": { "description": "Salty Snacks" }
|
||||
},
|
||||
"relationship": ""
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"url": "/api/v1/event_log/1/",
|
||||
"related": {
|
||||
"user": "/users/N/",
|
||||
"object1": "/organizations/N/",
|
||||
"object2": ""
|
||||
},
|
||||
|
||||
"summary_fields": {
|
||||
"object1": {
|
||||
"name": "Frito Lay",
|
||||
"description": "Salty Snacks"
|
||||
},
|
||||
"user": {
|
||||
"username": "chouseknecht"
|
||||
}
|
||||
},
|
||||
|
||||
"created": "2013-11-06T15:18:58.391Z",
|
||||
"modified": "2013-11-06T15:18:58.514Z",
|
||||
"user": 1,
|
||||
"event_time": "2013-11-06T15:18:58.514Z",
|
||||
"operation": "change",
|
||||
"changes": {
|
||||
"before": { "description": "Healthy Snacks" },
|
||||
"after": { "description": "Salty Snacks" }
|
||||
},
|
||||
"relationship": ""
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"url": "/api/v1/event_log/1/",
|
||||
"related": {
|
||||
"user": "/users/N/",
|
||||
"object1": "/organizations/N/",
|
||||
"object2": ""
|
||||
},
|
||||
|
||||
"summary_fields": {
|
||||
"object1": {
|
||||
"name": "Frito Lay",
|
||||
"description": "Salty Snacks"
|
||||
},
|
||||
"user": {
|
||||
"username": "chouseknecht"
|
||||
}
|
||||
},
|
||||
|
||||
"created": "2013-11-06T15:18:58.391Z",
|
||||
"modified": "2013-11-06T15:18:58.514Z",
|
||||
"user": 1,
|
||||
"event_time": "2013-11-06T15:18:58.514Z",
|
||||
"operation": "change",
|
||||
"changes": {
|
||||
"before": { "description": "Healthy Snacks" },
|
||||
"after": { "description": "Salty Snacks" }
|
||||
},
|
||||
"relationship": ""
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"url": "/api/v1/event_log/1/",
|
||||
"related": {
|
||||
"user": "/users/N/",
|
||||
"object1": "/organizations/N/",
|
||||
"object2": ""
|
||||
},
|
||||
|
||||
"summary_fields": {
|
||||
"object1": {
|
||||
"name": "Frito Lay",
|
||||
"description": "Salty Snacks"
|
||||
},
|
||||
"user": {
|
||||
"username": "chouseknecht"
|
||||
}
|
||||
},
|
||||
|
||||
"created": "2013-11-06T15:18:58.391Z",
|
||||
"modified": "2013-11-06T15:18:58.514Z",
|
||||
"user": 1,
|
||||
"event_time": "2013-11-06T15:18:58.514Z",
|
||||
"operation": "change",
|
||||
"changes": {
|
||||
"before": { "description": "Healthy Snacks" },
|
||||
"after": { "description": "Salty Snacks" }
|
||||
},
|
||||
"relationship": ""
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"url": "/api/v1/event_log/1/",
|
||||
"related": {
|
||||
"user": "/users/N/",
|
||||
"object1": "/organizations/N/",
|
||||
"object2": ""
|
||||
},
|
||||
|
||||
"summary_fields": {
|
||||
"object1": {
|
||||
"name": "Frito Lay",
|
||||
"description": "Salty Snacks"
|
||||
},
|
||||
"user": {
|
||||
"username": "chouseknecht"
|
||||
}
|
||||
},
|
||||
|
||||
"created": "2013-11-06T15:18:58.391Z",
|
||||
"modified": "2013-11-06T15:18:58.514Z",
|
||||
"user": 1,
|
||||
"event_time": "2013-11-06T15:18:58.514Z",
|
||||
"operation": "change",
|
||||
"changes": {
|
||||
"before": { "description": "Healthy Snacks" },
|
||||
"after": { "description": "Salty Snacks" }
|
||||
},
|
||||
"relationship": ""
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"url": "/api/v1/event_log/1/",
|
||||
"related": {
|
||||
"user": "/users/N/",
|
||||
"object1": "/organizations/N/",
|
||||
"object2": ""
|
||||
},
|
||||
|
||||
"summary_fields": {
|
||||
"object1": {
|
||||
"name": "Frito Lay",
|
||||
"description": "Salty Snacks"
|
||||
},
|
||||
"user": {
|
||||
"username": "chouseknecht"
|
||||
}
|
||||
},
|
||||
|
||||
"created": "2013-11-06T15:18:58.391Z",
|
||||
"modified": "2013-11-06T15:18:58.514Z",
|
||||
"user": 1,
|
||||
"event_time": "2013-11-06T15:18:58.514Z",
|
||||
"operation": "change",
|
||||
"changes": {
|
||||
"before": { "description": "Healthy Snacks" },
|
||||
"after": { "description": "Salty Snacks" }
|
||||
},
|
||||
"relationship": ""
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"url": "/api/v1/event_log/1/",
|
||||
"related": {
|
||||
"user": "/users/N/",
|
||||
"object1": "/organizations/N/",
|
||||
"object2": ""
|
||||
},
|
||||
|
||||
"summary_fields": {
|
||||
"object1": {
|
||||
"name": "Frito Lay",
|
||||
"description": "Salty Snacks"
|
||||
},
|
||||
"user": {
|
||||
"username": "chouseknecht"
|
||||
}
|
||||
},
|
||||
|
||||
"created": "2013-11-06T15:18:58.391Z",
|
||||
"modified": "2013-11-06T15:18:58.514Z",
|
||||
"user": 1,
|
||||
"event_time": "2013-11-06T15:18:58.514Z",
|
||||
"operation": "change",
|
||||
"changes": {
|
||||
"before": { "description": "Healthy Snacks" },
|
||||
"after": { "description": "Salty Snacks" }
|
||||
},
|
||||
"relationship": ""
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"url": "/api/v1/event_log/1/",
|
||||
"related": {
|
||||
"user": "/users/N/",
|
||||
"object1": "/organizations/N/",
|
||||
"object2": ""
|
||||
},
|
||||
|
||||
"summary_fields": {
|
||||
"object1": {
|
||||
"name": "Frito Lay",
|
||||
"description": "Salty Snacks"
|
||||
},
|
||||
"user": {
|
||||
"username": "chouseknecht"
|
||||
}
|
||||
},
|
||||
|
||||
"created": "2013-11-06T15:18:58.391Z",
|
||||
"modified": "2013-11-06T15:18:58.514Z",
|
||||
"user": 1,
|
||||
"event_time": "2013-11-06T15:18:58.514Z",
|
||||
"operation": "change",
|
||||
"changes": {
|
||||
"before": { "description": "Healthy Snacks" },
|
||||
"after": { "description": "Salty Snacks" }
|
||||
},
|
||||
"relationship": ""
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"url": "/api/v1/event_log/1/",
|
||||
"related": {
|
||||
"user": "/users/N/",
|
||||
"object1": "/organizations/N/",
|
||||
"object2": ""
|
||||
},
|
||||
|
||||
"summary_fields": {
|
||||
"object1": {
|
||||
"name": "Frito Lay",
|
||||
"description": "Salty Snacks"
|
||||
},
|
||||
"user": {
|
||||
"username": "chouseknecht"
|
||||
}
|
||||
},
|
||||
|
||||
"created": "2013-11-06T15:18:58.391Z",
|
||||
"modified": "2013-11-06T15:18:58.514Z",
|
||||
"user": 1,
|
||||
"event_time": "2013-11-06T15:18:58.514Z",
|
||||
"operation": "change",
|
||||
"changes": {
|
||||
"before": { "description": "Healthy Snacks" },
|
||||
"after": { "description": "Salty Snacks" }
|
||||
},
|
||||
"relationship": ""
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"url": "/api/v1/event_log/1/",
|
||||
"related": {
|
||||
"user": "/users/N/",
|
||||
"object1": "/organizations/N/",
|
||||
"object2": ""
|
||||
},
|
||||
|
||||
"summary_fields": {
|
||||
"object1": {
|
||||
"name": "Frito Lay",
|
||||
"description": "Salty Snacks"
|
||||
},
|
||||
"user": {
|
||||
"username": "chouseknecht"
|
||||
}
|
||||
},
|
||||
|
||||
"created": "2013-11-06T15:18:58.391Z",
|
||||
"modified": "2013-11-06T15:18:58.514Z",
|
||||
"user": 1,
|
||||
"event_time": "2013-11-06T15:18:58.514Z",
|
||||
"operation": "change",
|
||||
"changes": {
|
||||
"before": { "description": "Healthy Snacks" },
|
||||
"after": { "description": "Salty Snacks" }
|
||||
},
|
||||
"relationship": ""
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"url": "/api/v1/event_log/1/",
|
||||
"related": {
|
||||
"user": "/users/N/",
|
||||
"object1": "/organizations/N/",
|
||||
"object2": ""
|
||||
},
|
||||
|
||||
"summary_fields": {
|
||||
"object1": {
|
||||
"name": "Frito Lay",
|
||||
"description": "Salty Snacks"
|
||||
},
|
||||
"user": {
|
||||
"username": "chouseknecht"
|
||||
}
|
||||
},
|
||||
|
||||
"created": "2013-11-06T15:18:58.391Z",
|
||||
"modified": "2013-11-06T15:18:58.514Z",
|
||||
"user": 1,
|
||||
"event_time": "2013-11-06T15:18:58.514Z",
|
||||
"operation": "change",
|
||||
"changes": {
|
||||
"before": { "description": "Healthy Snacks" },
|
||||
"after": { "description": "Salty Snacks" }
|
||||
},
|
||||
"relationship": ""
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"url": "/api/v1/event_log/1/",
|
||||
"related": {
|
||||
"user": "/users/N/",
|
||||
"object1": "/organizations/N/",
|
||||
"object2": ""
|
||||
},
|
||||
|
||||
"summary_fields": {
|
||||
"object1": {
|
||||
"name": "Frito Lay",
|
||||
"description": "Salty Snacks"
|
||||
},
|
||||
"user": {
|
||||
"username": "chouseknecht"
|
||||
}
|
||||
},
|
||||
|
||||
"created": "2013-11-06T15:18:58.391Z",
|
||||
"modified": "2013-11-06T15:18:58.514Z",
|
||||
"user": 1,
|
||||
"event_time": "2013-11-06T15:18:58.514Z",
|
||||
"operation": "change",
|
||||
"changes": {
|
||||
"before": { "description": "Healthy Snacks" },
|
||||
"after": { "description": "Salty Snacks" }
|
||||
},
|
||||
"relationship": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 53 KiB |
BIN
awx/ui/static/img/footsteps.png
Normal file
BIN
awx/ui/static/img/footsteps.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.4 KiB |
@ -5,7 +5,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
var urlPrefix = '/static/';
|
||||
var urlPrefix = $basePath;
|
||||
|
||||
angular.module('ansible', [
|
||||
'RestServices',
|
||||
@ -74,13 +74,16 @@ angular.module('ansible', [
|
||||
'InventorySyncStatusWidget',
|
||||
'SCMSyncStatusWidget',
|
||||
'ObjectCountWidget',
|
||||
'StreamWidget',
|
||||
'JobsHelper',
|
||||
'InventoryStatusDefinition',
|
||||
'InventorySummaryHelpDefinition',
|
||||
'InventoryHostsHelpDefinition',
|
||||
'TreeSelector',
|
||||
'CredentialsHelper',
|
||||
'TimerService'
|
||||
'TimerService',
|
||||
'StreamListDefinition',
|
||||
'HomeGroupListDefinition'
|
||||
])
|
||||
.config(['$routeProvider', function($routeProvider) {
|
||||
$routeProvider.
|
||||
@ -245,13 +248,15 @@ angular.module('ansible', [
|
||||
when('/logout', { templateUrl: urlPrefix + 'partials/organizations.html', controller: Authenticate }).
|
||||
|
||||
when('/home', { templateUrl: urlPrefix + 'partials/home.html', controller: Home }).
|
||||
|
||||
when('/home/groups', { templateUrl: urlPrefix + 'partials/subhome.html', controller: HomeGroups }).
|
||||
|
||||
otherwise({redirectTo: '/home'});
|
||||
}])
|
||||
.run(['$cookieStore', '$rootScope', 'CheckLicense', '$location', 'Authorization','LoadBasePaths', 'ViewLicense',
|
||||
'Timer',
|
||||
'Timer', 'ClearScope', 'HideStream',
|
||||
function($cookieStore, $rootScope, CheckLicense, $location, Authorization, LoadBasePaths, ViewLicense,
|
||||
Timer) {
|
||||
Timer, ClearScope, HideStream) {
|
||||
|
||||
LoadBasePaths();
|
||||
|
||||
@ -260,12 +265,17 @@ angular.module('ansible', [
|
||||
$rootScope.sessionTimer = Timer.init();
|
||||
|
||||
$rootScope.$on("$routeChangeStart", function(event, next, current) {
|
||||
|
||||
// Before navigating away from current tab, make sure the primary view is visible
|
||||
if ($('#stream-container').is(':visible')) {
|
||||
HideStream();
|
||||
}
|
||||
|
||||
// On each navigation request, check that the user is logged in
|
||||
|
||||
var tst = /login/;
|
||||
var tst = /(login|logout)/;
|
||||
var path = $location.path();
|
||||
if ( !tst.test($location.path()) ) {
|
||||
// capture most recent URL, excluding login
|
||||
// capture most recent URL, excluding login/logout
|
||||
$rootScope.lastPath = path;
|
||||
$cookieStore.put('lastPath', path);
|
||||
}
|
||||
@ -288,11 +298,6 @@ angular.module('ansible', [
|
||||
CheckLicense();
|
||||
}
|
||||
|
||||
if ($rootScope.timer) {
|
||||
clearInterval($rootScope.timer);
|
||||
$rootScope.timer = null;
|
||||
}
|
||||
|
||||
// Make the correct tab active
|
||||
var base = $location.path().replace(/^\//,'').split('/')[0];
|
||||
if (base == '') {
|
||||
|
||||
@ -227,6 +227,9 @@ function CredentialsAdd ($scope, $rootScope, $compile, $location, $log, $routePa
|
||||
data['username'] = scope['access_key'];
|
||||
data['password'] = scope['secret_key'];
|
||||
break;
|
||||
case 'scm':
|
||||
data['ssh_key_unlock'] = scope['scm_key_unlock'];
|
||||
break;
|
||||
}
|
||||
|
||||
if (Empty(data.team) && Empty(data.user)) {
|
||||
@ -415,7 +418,10 @@ function CredentialsEdit ($scope, $rootScope, $compile, $location, $log, $routeP
|
||||
scope['ssh_password'] = data.password;
|
||||
master['ssh_username'] = scope['ssh_username'];
|
||||
master['ssh_password'] = scope['ssh_password'];
|
||||
break;
|
||||
break;
|
||||
case 'scm':
|
||||
scope['scm_key_unlock'] = data['ssh_key_unlock'];
|
||||
break;
|
||||
}
|
||||
|
||||
scope.$emit('credentialLoaded');
|
||||
@ -451,7 +457,6 @@ function CredentialsEdit ($scope, $rootScope, $compile, $location, $log, $routeP
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!Empty(scope.team)) {
|
||||
data.team = scope.team;
|
||||
data.user = "";
|
||||
@ -472,6 +477,9 @@ function CredentialsEdit ($scope, $rootScope, $compile, $location, $log, $routeP
|
||||
data['username'] = scope['access_key'];
|
||||
data['password'] = scope['secret_key'];
|
||||
break;
|
||||
case 'scm':
|
||||
data['ssh_key_unlock'] = scope['scm_key_unlock'];
|
||||
break;
|
||||
}
|
||||
|
||||
if (Empty(data.team) && Empty(data.user)) {
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
'use strict';
|
||||
|
||||
function Home ($routeParams, $scope, $rootScope, $location, Wait, ObjectCount, JobStatus, InventorySyncStatus, SCMSyncStatus,
|
||||
ClearScope) {
|
||||
ClearScope, Stream) {
|
||||
|
||||
ClearScope('home'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
|
||||
//scope.
|
||||
@ -28,6 +28,8 @@ function Home ($routeParams, $scope, $rootScope, $location, Wait, ObjectCount, J
|
||||
InventorySyncStatus({ target: 'container2' });
|
||||
SCMSyncStatus({ target: 'container4' });
|
||||
ObjectCount({ target: 'container3' });
|
||||
|
||||
$rootScope.showActivity = function() { Stream(); }
|
||||
|
||||
$rootScope.$on('WidgetLoaded', function() {
|
||||
// Once all the widgets report back 'loaded', turn off Wait widget
|
||||
@ -39,4 +41,82 @@ function Home ($routeParams, $scope, $rootScope, $location, Wait, ObjectCount, J
|
||||
}
|
||||
|
||||
Home.$inject=[ '$routeParams', '$scope', '$rootScope', '$location', 'Wait', 'ObjectCount', 'JobStatus', 'InventorySyncStatus',
|
||||
'SCMSyncStatus', 'ClearScope'];
|
||||
'SCMSyncStatus', 'ClearScope', 'Stream'];
|
||||
|
||||
|
||||
function HomeGroups ($location, $routeParams, HomeGroupList, GenerateList, ProcessErrors, LoadBreadCrumbs, ReturnToCaller, ClearScope,
|
||||
GetBasePath, SearchInit, PaginateInit, FormatDate, HostsStatusMsg, UpdateStatusMsg, ViewUpdateStatus) {
|
||||
|
||||
ClearScope('htmlTemplate'); //Garbage collection. Don't leave behind any listeners/watchers from the prior
|
||||
//scope.
|
||||
|
||||
var generator = GenerateList;
|
||||
var list = HomeGroupList;
|
||||
var defaultUrl=GetBasePath('groups');
|
||||
|
||||
var scope = generator.inject(list, { mode: 'edit' });
|
||||
var base = $location.path().replace(/^\//,'').split('/')[0];
|
||||
|
||||
if (scope.removePostRefresh) {
|
||||
scope.removePostRefresh();
|
||||
}
|
||||
scope.removePostRefresh = scope.$on('PostRefresh', function() {
|
||||
var msg, update_status, last_update;
|
||||
for (var i=0; i < scope.groups.length; i++) {
|
||||
|
||||
scope['groups'][i]['inventory_name'] = scope['groups'][i]['summary_fields']['inventory']['name'];
|
||||
|
||||
last_update = (scope.groups[i].summary_fields.inventory_source.last_updated == null) ? null :
|
||||
FormatDate(new Date(scope.groups[i].summary_fields.inventory_source.last_updated));
|
||||
|
||||
// Set values for Failed Hosts column
|
||||
scope.groups[i].failed_hosts = scope.groups[i].hosts_with_active_failures + ' / ' + scope.groups[i].total_hosts;
|
||||
|
||||
msg = HostsStatusMsg({
|
||||
active_failures: scope.groups[i].hosts_with_active_failures,
|
||||
total_hosts: scope.groups[i].total_hosts,
|
||||
inventory_id: scope.groups[i].inventory
|
||||
});
|
||||
|
||||
update_status = UpdateStatusMsg({ status: scope.groups[i].summary_fields.inventory_source.status });
|
||||
|
||||
scope.groups[i].failed_hosts_tip = msg['tooltip'];
|
||||
scope.groups[i].failed_hosts_link = msg['url'];
|
||||
scope.groups[i].failed_hosts_class = msg['class'];
|
||||
scope.groups[i].status = update_status['status'];
|
||||
scope.groups[i].source = scope.groups[i].summary_fields.inventory_source.source;
|
||||
scope.groups[i].last_updated = last_update;
|
||||
scope.groups[i].status_badge_class = update_status['class'];
|
||||
scope.groups[i].status_badge_tooltip = update_status['tooltip'];
|
||||
}
|
||||
});
|
||||
|
||||
SearchInit({ scope: scope, set: 'groups', list: list, url: defaultUrl });
|
||||
PaginateInit({ scope: scope, list: list, url: defaultUrl });
|
||||
|
||||
if ($routeParams['status']) {
|
||||
// with status param, called post update-submit
|
||||
scope[list.iterator + 'SearchField'] = 'status';
|
||||
scope[list.iterator + 'SelectShow'] = true;
|
||||
scope[list.iterator + 'SearchSelectOpts'] = list.fields['status'].searchOptions;
|
||||
scope[list.iterator + 'SearchFieldLabel'] = list.fields['status'].label.replace(/\<br\>/g,' ');
|
||||
for (var opt in list.fields['status'].searchOptions) {
|
||||
if (list.fields['status'].searchOptions[opt].value == $routeParams['status']) {
|
||||
scope[list.iterator + 'SearchSelectValue'] = list.fields['status'].searchOptions[opt];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scope.search(list.iterator);
|
||||
|
||||
LoadBreadCrumbs();
|
||||
|
||||
scope.viewUpdateStatus = function(id) { ViewUpdateStatus({ scope: scope, group_id: id }) };
|
||||
|
||||
}
|
||||
|
||||
HomeGroups.$inject = [ '$location', '$routeParams', 'HomeGroupList', 'GenerateList', 'ProcessErrors', 'LoadBreadCrumbs', 'ReturnToCaller',
|
||||
'ClearScope', 'GetBasePath', 'SearchInit', 'PaginateInit', 'FormatDate', 'HostsStatusMsg', 'UpdateStatusMsg', 'ViewUpdateStatus'
|
||||
];
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
function JobsListCtrl ($scope, $rootScope, $location, $log, $routeParams, Rest, Alert, JobList,
|
||||
GenerateList, LoadBreadCrumbs, Prompt, SearchInit, PaginateInit, ReturnToCaller,
|
||||
ClearScope, ProcessErrors, GetBasePath, LookUpInit, SubmitJob, FormatDate, Refresh,
|
||||
JobStatusToolTip)
|
||||
JobStatusToolTip, Empty)
|
||||
{
|
||||
ClearScope('htmlTemplate');
|
||||
var list = JobList;
|
||||
@ -52,7 +52,7 @@ function JobsListCtrl ($scope, $rootScope, $location, $log, $routeParams, Rest,
|
||||
if ($routeParams['job_host_summaries__host']) {
|
||||
defaultUrl += '?job_host_summaries__host=' + $routeParams['job_host_summaries__host'];
|
||||
}
|
||||
if ($routeParams['inventory__int'] && $routeParams['status']) {
|
||||
else if ($routeParams['inventory__int'] && $routeParams['status']) {
|
||||
defaultUrl += '?inventory__int=' + $routeParams['inventory__int'] + '&status=' +
|
||||
$routeParams['status'];
|
||||
}
|
||||
@ -70,6 +70,18 @@ function JobsListCtrl ($scope, $rootScope, $location, $log, $routeParams, Rest,
|
||||
scope[list.iterator + 'SearchValue'] = $routeParams['id__int'];
|
||||
scope[list.iterator + 'SearchFieldLabel'] = 'Job ID';
|
||||
}
|
||||
if ($routeParams['status']) {
|
||||
scope[list.iterator + 'SearchField'] = 'status';
|
||||
scope[list.iterator + 'SelectShow'] = true;
|
||||
scope[list.iterator + 'SearchSelectOpts'] = list.fields['status'].searchOptions;
|
||||
scope[list.iterator + 'SearchFieldLabel'] = list.fields['status'].label.replace(/\<br\>/g,' ');
|
||||
for (var opt in list.fields['status'].searchOptions) {
|
||||
if (list.fields['status'].searchOptions[opt].value == $routeParams['status']) {
|
||||
scope[list.iterator + 'SearchSelectValue'] = list.fields['status'].searchOptions[opt];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scope.search(list.iterator);
|
||||
|
||||
@ -162,7 +174,8 @@ function JobsListCtrl ($scope, $rootScope, $location, $log, $routeParams, Rest,
|
||||
|
||||
JobsListCtrl.$inject = [ '$scope', '$rootScope', '$location', '$log', '$routeParams', 'Rest', 'Alert', 'JobList',
|
||||
'GenerateList', 'LoadBreadCrumbs', 'Prompt', 'SearchInit', 'PaginateInit', 'ReturnToCaller', 'ClearScope',
|
||||
'ProcessErrors','GetBasePath', 'LookUpInit', 'SubmitJob', 'FormatDate', 'Refresh', 'JobStatusToolTip'
|
||||
'ProcessErrors','GetBasePath', 'LookUpInit', 'SubmitJob', 'FormatDate', 'Refresh', 'JobStatusToolTip',
|
||||
'Empty'
|
||||
];
|
||||
|
||||
|
||||
|
||||
@ -186,6 +186,26 @@ angular.module('CredentialFormDefinition', [])
|
||||
awPassMatch: true,
|
||||
associated: 'ssh_key_unlock'
|
||||
},
|
||||
"scm_key_unlock": {
|
||||
label: 'Key Password',
|
||||
type: 'password',
|
||||
ngShow: "kind.value == 'scm'",
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
ngChange: "clearPWConfirm('scm_key_unlock_confirm')",
|
||||
associated: 'scm_key_unlock_confirm',
|
||||
ask: false,
|
||||
clear: true
|
||||
},
|
||||
"scm_key_unlock_confirm": {
|
||||
label: 'Confirm Key Password',
|
||||
type: 'password',
|
||||
ngShow: "kind.value == 'scm'",
|
||||
addRequired: false,
|
||||
editRequired: false,
|
||||
awPassMatch: true,
|
||||
associated: 'scm_key_unlock'
|
||||
},
|
||||
"sudo_username": {
|
||||
label: 'Sudo Username',
|
||||
type: 'text',
|
||||
|
||||
@ -79,6 +79,49 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', '
|
||||
}
|
||||
}])
|
||||
|
||||
.factory('ViewUpdateStatus', [ 'Rest', 'ProcessErrors', 'GetBasePath', 'ShowUpdateStatus', 'Alert',
|
||||
function(Rest, ProcessErrors, GetBasePath, ShowUpdateStatus, Alert) {
|
||||
return function(params) {
|
||||
|
||||
var scope = params.scope;
|
||||
var id = params.group_id;
|
||||
var found = false;
|
||||
var group;
|
||||
for (var i=0; i < scope.groups.length; i++) {
|
||||
if (scope.groups[i].id == id) {
|
||||
found = true;
|
||||
group = scope.groups[i];
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
if (group.summary_fields.inventory_source.source == "" || group.summary_fields.inventory_source.source == null) {
|
||||
Alert('Missing Configuration', 'The selected group is not configured for inventory updates. ' +
|
||||
'You must first edit the group, provide Source settings, and then run an update.', 'alert-info');
|
||||
}
|
||||
else if (group.summary_fields.inventory_source.status == "" || group.summary_fields.inventory_source.status == null ||
|
||||
group.summary_fields.inventory_source.status == "never updated") {
|
||||
Alert('No Status Available', 'The inventory update process has not run for the selected group. Start the process by ' +
|
||||
'clicking the Update button.', 'alert-info');
|
||||
}
|
||||
else {
|
||||
Rest.setUrl(group.related.inventory_source);
|
||||
Rest.get()
|
||||
.success( function(data, status, headers, config) {
|
||||
var url = (data.related.current_update) ? data.related.current_update : data.related.last_update;
|
||||
ShowUpdateStatus({ group_name: data.summary_fields.group.name,
|
||||
last_update: url });
|
||||
})
|
||||
.error( function(data, status, headers, config) {
|
||||
ProcessErrors(scope, data, status, form,
|
||||
{ hdr: 'Error!', msg: 'Failed to retrieve inventory source: ' + group.related.inventory_source +
|
||||
' POST returned status: ' + status });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}])
|
||||
|
||||
.factory('HostsStatusMsg', [ function() {
|
||||
return function(params) {
|
||||
var active_failures = params.active_failures;
|
||||
@ -278,11 +321,11 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', '
|
||||
}])
|
||||
|
||||
.factory('InventoryStatus', [ '$rootScope', '$routeParams', 'Rest', 'Alert', 'ProcessErrors', 'GetBasePath', 'FormatDate', 'InventorySummary',
|
||||
'GenerateList', 'ClearScope', 'SearchInit', 'PaginateInit', 'Refresh', 'InventoryUpdate', 'GroupsEdit', 'ShowUpdateStatus', 'HelpDialog',
|
||||
'InventorySummaryHelp', 'BuildTree', 'ClickNode', 'HostsStatusMsg', 'UpdateStatusMsg',
|
||||
function($rootScope, $routeParams, Rest, Alert, ProcessErrors, GetBasePath, FormatDate, InventorySummary, GenerateList, ClearScope, SearchInit,
|
||||
PaginateInit, Refresh, InventoryUpdate, GroupsEdit, ShowUpdateStatus, HelpDialog, InventorySummaryHelp, BuildTree, ClickNode,
|
||||
HostsStatusMsg, UpdateStatusMsg) {
|
||||
'GenerateList', 'ClearScope', 'SearchInit', 'PaginateInit', 'Refresh', 'InventoryUpdate', 'GroupsEdit', 'HelpDialog',
|
||||
'InventorySummaryHelp', 'BuildTree', 'ClickNode', 'HostsStatusMsg', 'UpdateStatusMsg', 'ViewUpdateStatus',
|
||||
function($rootScope, $routeParams, Rest, Alert, ProcessErrors, GetBasePath, FormatDate, InventorySummary, GenerateList, ClearScope,
|
||||
SearchInit, PaginateInit, Refresh, InventoryUpdate, GroupsEdit, HelpDialog, InventorySummaryHelp, BuildTree, ClickNode,
|
||||
HostsStatusMsg, UpdateStatusMsg, ViewUpdateStatus) {
|
||||
return function(params) {
|
||||
//Build a summary of a given inventory
|
||||
|
||||
@ -373,41 +416,7 @@ angular.module('GroupsHelper', [ 'RestServices', 'Utilities', 'ListGenerator', '
|
||||
HelpDialog({ defn: InventorySummaryHelp });
|
||||
}
|
||||
|
||||
scope.viewUpdateStatus = function(id) {
|
||||
var found = false;
|
||||
var group;
|
||||
for (var i=0; i < scope.groups.length; i++) {
|
||||
if (scope.groups[i].id == id) {
|
||||
found = true;
|
||||
group = scope.groups[i];
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
if (group.summary_fields.inventory_source.source == "" || group.summary_fields.inventory_source.source == null) {
|
||||
Alert('Missing Configuration', 'The selected group is not configured for inventory updates. ' +
|
||||
'You must first edit the group, provide Source settings, and then run an update.', 'alert-info');
|
||||
}
|
||||
else if (group.summary_fields.inventory_source.status == "" || group.summary_fields.inventory_source.status == null ||
|
||||
group.summary_fields.inventory_source.status == "never updated") {
|
||||
Alert('No Status Available', 'The inventory update process has not run for the selected group. Start the process by ' +
|
||||
'clicking the Update button.', 'alert-info');
|
||||
}
|
||||
else {
|
||||
Rest.setUrl(group.related.inventory_source);
|
||||
Rest.get()
|
||||
.success( function(data, status, headers, config) {
|
||||
var url = (data.related.current_update) ? data.related.current_update : data.related.last_update;
|
||||
ShowUpdateStatus({ group_name: data.summary_fields.group.name,
|
||||
last_update: url });
|
||||
})
|
||||
.error( function(data, status, headers, config) {
|
||||
ProcessErrors(scope, data, status, form,
|
||||
{ hdr: 'Error!', msg: 'Failed to retrieve inventory source: ' + group.related.inventory_source +
|
||||
' POST returned status: ' + status });
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
scope.viewUpdateStatus = function(group_id) { ViewUpdateStatus({ scope: scope, group_id: group_id }) };
|
||||
|
||||
// Click on group name
|
||||
scope.GroupsEdit = function(group_id) {
|
||||
|
||||
119
awx/ui/static/js/lists/HomeGroups.js
Normal file
119
awx/ui/static/js/lists/HomeGroups.js
Normal file
@ -0,0 +1,119 @@
|
||||
/*********************************************
|
||||
* Copyright (c) 2013 AnsibleWorks, Inc.
|
||||
*
|
||||
* HomeGroups.js
|
||||
*
|
||||
* List view object for Group data model. Used
|
||||
* on the home tab.
|
||||
*
|
||||
*/
|
||||
angular.module('HomeGroupListDefinition', [])
|
||||
.value(
|
||||
'HomeGroupList', {
|
||||
|
||||
name: 'groups',
|
||||
iterator: 'group',
|
||||
editTitle: 'Groups',
|
||||
index: true,
|
||||
hover: true,
|
||||
|
||||
fields: {
|
||||
name: {
|
||||
key: true,
|
||||
label: 'Group',
|
||||
ngClick: "\{\{ 'GroupsEdit(' + group.id + ')' \}\}",
|
||||
columnClass: 'col-lg-3 col-md3 col-sm-2',
|
||||
linkTo: "\{\{ '/#/inventories/' + group.inventory + '/groups/?name=' + group.name \}\}"
|
||||
},
|
||||
inventory_name: {
|
||||
label: 'Inventory',
|
||||
sourceModel: 'inventory',
|
||||
sourceField: 'name',
|
||||
columnClass: 'col-lg-3 col-md3 col-sm-2',
|
||||
linkTo: "\{\{ '/#/inventories/' + group.inventory \}\}"
|
||||
},
|
||||
failed_hosts: {
|
||||
label: 'Failed Hosts',
|
||||
ngHref: "\{\{ group.failed_hosts_link \}\}",
|
||||
badgeIcon: "\{\{ 'icon-failures-' + group.failed_hosts_class \}\}",
|
||||
badgeNgHref: "\{\{ group.failed_hosts_link \}\}",
|
||||
badgePlacement: 'left',
|
||||
badgeToolTip: "\{\{ group.failed_hosts_tip \}\}",
|
||||
badgeTipPlacement: 'top',
|
||||
awToolTip: "\{\{ group.failed_hosts_tip \}\}",
|
||||
dataPlacement: "top",
|
||||
searchable: false,
|
||||
excludeModal: true,
|
||||
sortField: "hosts_with_active_failures"
|
||||
},
|
||||
status: {
|
||||
label: 'Status',
|
||||
ngClick: "viewUpdateStatus(\{\{ group.id \}\})",
|
||||
searchType: 'select',
|
||||
badgeIcon: "\{\{ 'icon-cloud-' + group.status_badge_class \}\}",
|
||||
badgeToolTip: "\{\{ group.status_badge_tooltip \}\}",
|
||||
awToolTip: "\{\{ group.status_badge_tooltip \}\}",
|
||||
dataPlacement: 'top',
|
||||
badgeTipPlacement: 'top',
|
||||
badgePlacement: 'left',
|
||||
searchOptions: [
|
||||
{ name: "failed", value: "failed" },
|
||||
{ name: "never", value: "never updated" },
|
||||
{ name: "n/a", value: "none" },
|
||||
{ name: "successful", value: "successful" },
|
||||
{ name: "updating", value: "updating" }],
|
||||
sourceModel: 'inventory_source',
|
||||
sourceField: 'status'
|
||||
},
|
||||
last_updated: {
|
||||
label: 'Last<br>Updated',
|
||||
sourceModel: 'inventory_source',
|
||||
sourceField: 'last_updated',
|
||||
searchable: false,
|
||||
nosort: false
|
||||
},
|
||||
source: {
|
||||
label: 'Source',
|
||||
searchType: 'select',
|
||||
searchOptions: [
|
||||
{ name: "ec2", value: "ec2" },
|
||||
{ name: "none", value: "" },
|
||||
{ name: "rackspace", value: "rackspace" }],
|
||||
sourceModel: 'inventory_source',
|
||||
sourceField: 'source',
|
||||
searchOnly: true
|
||||
},
|
||||
has_external_source: {
|
||||
label: 'Has external source?',
|
||||
searchType: 'in',
|
||||
searchValue: 'ec2,rackspace',
|
||||
searchOnly: true,
|
||||
sourceModel: 'inventory_source',
|
||||
sourceField: 'source'
|
||||
},
|
||||
has_active_failures: {
|
||||
label: 'Has failed hosts?',
|
||||
searchSingleValue: true,
|
||||
searchType: 'boolean',
|
||||
searchValue: 'true',
|
||||
searchOnly: true
|
||||
},
|
||||
last_update_failed: {
|
||||
label: 'Update failed?',
|
||||
searchType: 'select',
|
||||
searchSingleValue: true,
|
||||
searchValue: 'failed',
|
||||
searchOnly: true,
|
||||
sourceModel: 'inventory_source',
|
||||
sourceField: 'status'
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
|
||||
},
|
||||
|
||||
fieldActions: {
|
||||
|
||||
}
|
||||
});
|
||||
@ -133,7 +133,8 @@ angular.module('InventorySummaryDefinition', [])
|
||||
mode: 'all',
|
||||
'class': 'btn-xs btn-primary',
|
||||
awToolTip: "Refresh the page",
|
||||
ngClick: "refresh()"
|
||||
ngClick: "refresh()",
|
||||
iconSize: 'large'
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@ -92,7 +92,8 @@ angular.module('JobEventsListDefinition', [])
|
||||
ngShow: "job_status == 'pending' || job_status == 'waiting' || job_status == 'running'",
|
||||
'class': 'btn-xs btn-primary',
|
||||
awToolTip: "Refresh the page",
|
||||
ngClick: "refresh()"
|
||||
ngClick: "refresh()",
|
||||
iconSize: 'large'
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@ -126,7 +126,8 @@ angular.module('JobHostDefinition', [])
|
||||
ngShow: "host_id == null && (job_status == 'pending' || job_status == 'waiting' || job_status == 'running')",
|
||||
'class': 'btn-xs btn-primary',
|
||||
awToolTip: "Refresh the page",
|
||||
ngClick: "refresh()"
|
||||
ngClick: "refresh()",
|
||||
iconSize: 'large'
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@ -81,7 +81,8 @@ angular.module('JobsListDefinition', [])
|
||||
mode: 'all',
|
||||
'class': 'btn-xs btn-primary',
|
||||
awToolTip: "Refresh the page",
|
||||
ngClick: "refresh()"
|
||||
ngClick: "refresh()",
|
||||
iconSize: 'large'
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@ -78,7 +78,8 @@ angular.module('ProjectsListDefinition', [])
|
||||
mode: 'all',
|
||||
'class': 'btn-xs btn-primary',
|
||||
awToolTip: "Refresh the page",
|
||||
ngClick: "refresh()"
|
||||
ngClick: "refresh()",
|
||||
iconSize: 'large'
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
62
awx/ui/static/js/lists/Streams.js
Normal file
62
awx/ui/static/js/lists/Streams.js
Normal file
@ -0,0 +1,62 @@
|
||||
/*********************************************
|
||||
* Copyright (c) 2013 AnsibleWorks, Inc.
|
||||
*
|
||||
* Streams.js
|
||||
* List view object for activity stream data model.
|
||||
*
|
||||
*
|
||||
*/
|
||||
angular.module('StreamListDefinition', [])
|
||||
.value(
|
||||
'StreamList', {
|
||||
|
||||
name: 'activities',
|
||||
iterator: 'activity',
|
||||
editTitle: 'Activity Stream',
|
||||
selectInstructions: '',
|
||||
index: false,
|
||||
hover: true,
|
||||
"class": "table-condensed",
|
||||
|
||||
fields: {
|
||||
event_time: {
|
||||
key: true,
|
||||
label: 'When'
|
||||
},
|
||||
user: {
|
||||
label: 'Who',
|
||||
sourceModel: 'user',
|
||||
sourceField: 'username'
|
||||
},
|
||||
operation: {
|
||||
label: 'Operation'
|
||||
},
|
||||
description: {
|
||||
label: 'Description'
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
refresh: {
|
||||
dataPlacement: 'top',
|
||||
icon: "icon-refresh",
|
||||
mode: 'all',
|
||||
'class': 'btn-xs btn-primary',
|
||||
awToolTip: "Refresh the page",
|
||||
ngClick: "refreshStream()",
|
||||
iconSize: 'large'
|
||||
},
|
||||
close: {
|
||||
dataPlacement: 'top',
|
||||
icon: "icon-arrow-left",
|
||||
mode: 'all',
|
||||
'class': 'btn-xs btn-primary',
|
||||
awToolTip: "Close Activity Stream view",
|
||||
ngClick: "closeStream()",
|
||||
iconSize: 'large'
|
||||
}
|
||||
},
|
||||
|
||||
fieldActions: {
|
||||
}
|
||||
});
|
||||
@ -15,7 +15,7 @@ angular.module('JobStatusWidget', ['RestServices', 'Utilities'])
|
||||
var scope = $rootScope.$new();
|
||||
var jobCount, jobFails, inventoryCount, inventoryFails, groupCount, groupFails, hostCount, hostFails;
|
||||
var counts = 0;
|
||||
var expectedCounts = 8;
|
||||
var expectedCounts = 6;
|
||||
var target = params.target;
|
||||
|
||||
if (scope.removeCountReceived) {
|
||||
@ -25,17 +25,21 @@ angular.module('JobStatusWidget', ['RestServices', 'Utilities'])
|
||||
|
||||
var rowcount = 0;
|
||||
|
||||
function makeRow(label, count, fail) {
|
||||
function makeRow(params) {
|
||||
var html = '';
|
||||
var label = params.label;
|
||||
var link = params.link;
|
||||
var fail_link = params.fail_link;
|
||||
var count = params.count;
|
||||
var fail = params.fail;
|
||||
html += "<tr>\n";
|
||||
html += "<td><a href=\"/#/" + label.toLowerCase() + "\"";
|
||||
html += (label == 'Hosts' || label == 'Groups') ? " class=\"pad-left-sm\" " : "";
|
||||
html += "<td><a href=\"" + link + "\"";
|
||||
html += ">" + label + "</a></td>\n";
|
||||
html += "<td class=\"failed-column text-right\">";
|
||||
html += (fail > 0) ? "<a href=\"/blah/blah\">" + fail + "</a>" : "";
|
||||
html += "<a href=\"" + fail_link + "\">" + fail + "</a>";
|
||||
html += "</td>\n";
|
||||
html += "<td class=\"text-right\">"
|
||||
html += (count > 0) ? "<a href=\"/blah/blah\">" + count + "</a>" : "";
|
||||
html += "<a href=\"" + link + "\">" + count + "</a>";
|
||||
html += "</td></tr>\n";
|
||||
return html;
|
||||
}
|
||||
@ -57,19 +61,33 @@ angular.module('JobStatusWidget', ['RestServices', 'Utilities'])
|
||||
html += "<tbody>\n";
|
||||
|
||||
if (jobCount > 0) {
|
||||
html += makeRow('Jobs', jobCount, jobFails);
|
||||
rowcount++;
|
||||
}
|
||||
if (inventoryCount > 0) {
|
||||
html += makeRow('Inventories', inventoryCount, inventoryFails);
|
||||
html += makeRow({
|
||||
label: 'Jobs',
|
||||
link: '/#/jobs',
|
||||
count: jobCount,
|
||||
fail: jobFails,
|
||||
fail_link: '/#/jobs/?status=failed'
|
||||
});
|
||||
rowcount++;
|
||||
}
|
||||
if (groupCount > 0) {
|
||||
html += makeRow('Groups', groupCount, groupFails);
|
||||
html += makeRow({
|
||||
label: 'Groups',
|
||||
link: '/#/home/groups',
|
||||
count: groupCount,
|
||||
fail: groupFails,
|
||||
fail_link: '/#/home/groups/?status=failed'
|
||||
});
|
||||
rowcount++;
|
||||
}
|
||||
if (hostCount > 0) {
|
||||
html += makeRow('Hosts', hostCount, hostFails);
|
||||
html += makeRow({
|
||||
label: 'Hosts',
|
||||
link: '#/home/hosts',
|
||||
count: hostCount,
|
||||
fail: hostFails,
|
||||
fail_link: '/#/home/hosts/?status=failed'
|
||||
});
|
||||
rowcount++;
|
||||
}
|
||||
|
||||
@ -114,7 +132,7 @@ angular.module('JobStatusWidget', ['RestServices', 'Utilities'])
|
||||
{ hdr: 'Error!', msg: 'Failed to get ' + url + '. GET status: ' + status });
|
||||
});
|
||||
|
||||
url = GetBasePath('inventory') + '?page=1';
|
||||
/*url = GetBasePath('inventory') + '?page=1';
|
||||
Rest.setUrl(url);
|
||||
Rest.get()
|
||||
.success( function(data, status, headers, config) {
|
||||
@ -136,7 +154,7 @@ angular.module('JobStatusWidget', ['RestServices', 'Utilities'])
|
||||
.error( function(data, status, headers, config) {
|
||||
ProcessErrors(scope, data, status, null,
|
||||
{ hdr: 'Error!', msg: 'Failed to get ' + url + '. GET status: ' + status });
|
||||
});
|
||||
});*/
|
||||
|
||||
url = GetBasePath('groups') + '?page=1';
|
||||
Rest.setUrl(url);
|
||||
|
||||
102
awx/ui/static/js/widgets/Stream.js
Normal file
102
awx/ui/static/js/widgets/Stream.js
Normal file
@ -0,0 +1,102 @@
|
||||
/*********************************************
|
||||
* Copyright (c) 2013 AnsibleWorks, Inc.
|
||||
*
|
||||
* Stream.js
|
||||
*
|
||||
* Activity stream widget that can be called from anywhere
|
||||
*
|
||||
*/
|
||||
|
||||
angular.module('StreamWidget', ['RestServices', 'Utilities', 'StreamListDefinition', 'SearchHelper', 'PaginateHelper',
|
||||
'RefreshHelper', 'ListGenerator', 'StreamWidget'])
|
||||
|
||||
.factory('ShowStream', [ function() {
|
||||
return function() {
|
||||
// Slide in the Stream widget
|
||||
var stream = $('#stream-container');
|
||||
stream.css({
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: 0,
|
||||
width: '100%',
|
||||
'min-height': '100%',
|
||||
'background-color': '#FFF'
|
||||
});
|
||||
stream.show('slide', {'direction': 'left'}, {'duration': 500, 'queue': false });
|
||||
}
|
||||
}])
|
||||
|
||||
.factory('HideStream', [ 'ClearScope', function(ClearScope) {
|
||||
return function() {
|
||||
// Remove the stream widget
|
||||
var stream = $('#stream-container');
|
||||
stream.hide('slide', {'direction': 'left'}, {'duration': 500, 'queue': false });
|
||||
|
||||
// Completely destroy the container so we don't experience random flashes of it later.
|
||||
// There was some sort weirdness with the tab 'show' causing the stream to slide in when
|
||||
// a tab was clicked, after the stream had been hidden. Seemed like timing- wait long enough
|
||||
// before clicking a tab, and it would not happen.
|
||||
setTimeout( function() {
|
||||
stream.detach();
|
||||
stream.empty();
|
||||
stream.unbind();
|
||||
}, 500);
|
||||
}
|
||||
}])
|
||||
|
||||
.factory('Stream', ['$rootScope', '$location', 'Rest', 'GetBasePath', 'ProcessErrors', 'Wait', 'StreamList', 'SearchInit',
|
||||
'PaginateInit', 'GenerateList', 'FormatDate', 'ShowStream', 'HideStream',
|
||||
function($rootScope, $location, Rest, GetBasePath, ProcessErrors, Wait, StreamList, SearchInit, PaginateInit, GenerateList,
|
||||
FormatDate, ShowStream, HideStream) {
|
||||
return function(params) {
|
||||
|
||||
var list = StreamList;
|
||||
var defaultUrl = $basePath + 'html/event_log.html/';
|
||||
var view = GenerateList;
|
||||
|
||||
// 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('<div id="stream-container"><div id=\"stream-content\"></div></div><!-- Stream widget -->');
|
||||
|
||||
// Generate the list
|
||||
var scope = view.inject(list, {
|
||||
mode: 'edit',
|
||||
id: 'stream-content',
|
||||
breadCrumbs: true,
|
||||
searchSize: 'col-lg-4'
|
||||
});
|
||||
|
||||
scope.closeStream = function() {
|
||||
HideStream();
|
||||
}
|
||||
|
||||
scope.refreshStream = function() {
|
||||
scope['activities'].splice(10,10);
|
||||
//scope.search(list.iterator);
|
||||
}
|
||||
|
||||
if (scope.removePostRefresh) {
|
||||
scope.removePostRefresh();
|
||||
}
|
||||
scope.removePostRefresh = scope.$on('PostRefresh', function() {
|
||||
for (var i=0; i < scope['activities'].length; i++) {
|
||||
// Convert event_time date to local time zone
|
||||
cDate = new Date(scope['activities'][i].event_time);
|
||||
scope['activities'][i].event_time = FormatDate(cDate);
|
||||
// Display username
|
||||
scope['activities'][i].user = scope.activities[i].summary_fields.user.username;
|
||||
}
|
||||
ShowStream();
|
||||
});
|
||||
|
||||
// Initialize search and paginate pieces and load data
|
||||
SearchInit({ scope: scope, set: list.name, list: list, url: defaultUrl });
|
||||
PaginateInit({ scope: scope, list: list, url: defaultUrl });
|
||||
scope.search(list.iterator);
|
||||
|
||||
}
|
||||
}]);
|
||||
|
||||
@ -487,10 +487,10 @@ legend {
|
||||
margin: 10px 0 0 0;
|
||||
}
|
||||
|
||||
.page-size {
|
||||
height: 25px;
|
||||
font-size: 10.5px;
|
||||
line-height: normal;
|
||||
select.page-size {
|
||||
width: 65px;
|
||||
height: 24px;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.page-size-label {
|
||||
@ -642,7 +642,7 @@ input[type="checkbox"].checkbox-no-label {
|
||||
text-align: right;
|
||||
|
||||
button {
|
||||
margin-left: 8px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1374,6 +1374,31 @@ tr td button i {
|
||||
}
|
||||
|
||||
|
||||
/* Activity Stream Widget */
|
||||
|
||||
#stream-container {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#stream-content {
|
||||
border: 1px solid @grey;
|
||||
border-radius: 8px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
/*
|
||||
.activity-btn {
|
||||
padding-left: 2px;
|
||||
padding-right: 2px;
|
||||
padding-bottom: 2px;
|
||||
img {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
/* Large desktop */
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
|
||||
@ -109,14 +109,10 @@ angular.module('Utilities',['RestServices', 'Utilities'])
|
||||
}
|
||||
Alert(defaultMsg.hdr, msg);
|
||||
}
|
||||
else if (status == 401 && data.detail && data.detail == 'Token is expired') {
|
||||
else if ( (status == 401 && data.detail && data.detail == 'Token is expired') ||
|
||||
(status == 401 && data.detail && data.detail == 'Invalid token') ) {
|
||||
$rootScope.sessionTimer.expireSession();
|
||||
window.location = '/#/login'; //resetting location so that we drop search params
|
||||
}
|
||||
else if (status == 401 && data.detail && data.detail == 'Invalid token') {
|
||||
// should this condition be treated as an expired session?? Yes, for now.
|
||||
$rootScope.sessionTimer.expireSession();
|
||||
window.location = '/#/login'; //resetting location so that we drop search params
|
||||
$location.url('/login');
|
||||
}
|
||||
else if (data.non_field_errors) {
|
||||
Alert('Error!', data.non_field_errors);
|
||||
|
||||
@ -138,6 +138,7 @@ angular.module('GeneratorHelpers', ['GeneratorHelpers'])
|
||||
html += (btn.ngShow) ? Attr(btn, 'ngShow') : "";
|
||||
html += (btn.ngHide) ? Attr(btn, 'ngHide') : "";
|
||||
html += " >";
|
||||
html += (btn['img']) ? "<img src=\"" + $basePath + "img/" + btn.img + "\" style=\"width: 12px; height: 12px;\" >" : "";
|
||||
html += (btn['icon']) ? Attr(btn,'icon') : "";
|
||||
html += (btn['awRefresh'] && !btn['icon']) ? "<i class=\"icon-refresh\"></i> " : "";
|
||||
html += (btn.label) ? " " + btn.label : "";
|
||||
@ -595,7 +596,7 @@ angular.module('GeneratorHelpers', ['GeneratorHelpers'])
|
||||
html += "<select ng-model=\"" + iterator + "PageSize\" ng-change=\"changePageSize('" +
|
||||
set + "'," + "'" + iterator + "')\" ";
|
||||
html += "id=\"page_size_select\" ";
|
||||
html += "class=\"page-size\">\n";
|
||||
html += "class=\"page-size input-sm form-control\">\n";
|
||||
html += "<option value=\"10\" selected>10</option>\n";
|
||||
html += "<option value=\"20\" selected>20</option>\n";
|
||||
html += "<option value=\"40\">40</option>\n";
|
||||
|
||||
@ -55,6 +55,13 @@ angular.module('ListGenerator', ['GeneratorHelpers'])
|
||||
}
|
||||
this.setList(list);
|
||||
element.html(this.build(options)); // Inject the html
|
||||
if (options.prepend) { // Add any extra HTML passed in options
|
||||
element.prepend(options.prepend);
|
||||
}
|
||||
if (options.append) {
|
||||
element.append(options.prepend);
|
||||
}
|
||||
|
||||
this.scope = element.scope(); // Set scope specific to the element we're compiling, avoids circular reference
|
||||
// From here use 'scope' to manipulate the form, as the form is not in '$scope'
|
||||
$compile(element)(this.scope);
|
||||
@ -170,7 +177,7 @@ angular.module('ListGenerator', ['GeneratorHelpers'])
|
||||
html += "<strong>Hint: </strong>" + list.editInstructions + "\n";
|
||||
html += "</div>\n";
|
||||
}
|
||||
|
||||
|
||||
if (options.mode != 'lookup' && (list.well == undefined || list.well == true)) {
|
||||
html += "<div class=\"well\">\n";
|
||||
}
|
||||
@ -186,7 +193,10 @@ angular.module('ListGenerator', ['GeneratorHelpers'])
|
||||
}
|
||||
*/
|
||||
|
||||
if (options.mode == 'summary') {
|
||||
if (options.searchSize) {
|
||||
html += SearchWidget({ iterator: list.iterator, template: list, mini: true , size: options.searchSize });
|
||||
}
|
||||
else if (options.mode == 'summary') {
|
||||
html += SearchWidget({ iterator: list.iterator, template: list, mini: true , size: 'col-lg-6' });
|
||||
}
|
||||
else if (options.mode == 'lookup' || options.id != undefined) {
|
||||
@ -200,8 +210,13 @@ angular.module('ListGenerator', ['GeneratorHelpers'])
|
||||
//actions
|
||||
var base = $location.path().replace(/^\//,'').split('/')[0];
|
||||
|
||||
html += "<div class=\"";
|
||||
if (options.mode == 'summary') {
|
||||
html += "<div class=\"";
|
||||
if (options.searchSize) {
|
||||
// User supplied searchSize, calc the remaining
|
||||
var size = parseInt(options.searchSize.replace(/([A-Z]|[a-z]|\-)/g,''));
|
||||
html += 'col-lg-' + (11 - size);
|
||||
}
|
||||
else if (options.mode == 'summary') {
|
||||
html += 'col-lg-5';
|
||||
}
|
||||
else if (options.id != undefined) {
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
<div class="tab-pane" id="home">
|
||||
<div id="refresh-row" class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="refresh-grp pull-right">
|
||||
<button type="button" class="btn btn-primary btn-xs refresh-btn" ng-click="refreshCnt = 10; refresh()" id="refresh_btn" aw-tool-tip="Refresh page" data-placement="top" data-original-title="" title=""><i class="icon-refresh"></i></button>
|
||||
<div class="list-actions pull-right">
|
||||
<button type="button" class="btn btn-primary btn-xs refresh-btn" ng-click="refresh()" id="refresh_btn" aw-tool-tip="Refresh page" data-placement="top"><i class="icon-refresh icon-large"></i></button>
|
||||
<button type="button" class="btn btn-primary btn-xs activity-btn" ng-click="showActivity()" id="activity_btn" aw-tool-tip="View activity stream" data-placement="top"><i class="icon-comments-alt icon-large"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
3
awx/ui/static/partials/subhome.html
Normal file
3
awx/ui/static/partials/subhome.html
Normal file
@ -0,0 +1,3 @@
|
||||
<div class="tab-pane" id="home">
|
||||
<div id="htmlTemplate"></div>
|
||||
</div>
|
||||
@ -90,6 +90,8 @@
|
||||
<script src="{{ STATIC_URL }}js/lists/JobEvents.js"></script>
|
||||
<script src="{{ STATIC_URL }}js/lists/JobHosts.js"></script>
|
||||
<script src="{{ STATIC_URL }}js/lists/Permissions.js"></script>
|
||||
<script src="{{ STATIC_URL }}js/lists/Streams.js"></script>
|
||||
<script src="{{ STATIC_URL }}js/lists/HomeGroups.js"></script>
|
||||
<script src="{{ STATIC_URL }}js/helpers/refresh-related.js"></script>
|
||||
<script src="{{ STATIC_URL }}js/helpers/related-paginate.js"></script>
|
||||
<script src="{{ STATIC_URL }}js/helpers/related-search.js"></script>
|
||||
@ -117,13 +119,14 @@
|
||||
<script src="{{ STATIC_URL }}js/widgets/InventorySyncStatus.js"></script>
|
||||
<script src="{{ STATIC_URL }}js/widgets/SCMSyncStatus.js"></script>
|
||||
<script src="{{ STATIC_URL }}js/widgets/ObjectCount.js"></script>
|
||||
<script src="{{ STATIC_URL }}js/widgets/Stream.js"></script>
|
||||
<script src="{{ STATIC_URL }}js/help/InventorySummary.js"></script>
|
||||
<script src="{{ STATIC_URL }}js/help/InventoryHosts.js"></script>
|
||||
<script src="{{ STATIC_URL }}lib/less/less-1.4.1.min.js"></script>
|
||||
|
||||
{% endif %}
|
||||
|
||||
</head>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="navbar navbar-inverse navbar-fixed-top main-menu" role="navigation">
|
||||
@ -149,7 +152,7 @@
|
||||
</div>
|
||||
</div><!-- navbar -->
|
||||
|
||||
<div class="container main-container">
|
||||
<div class="container main-container" id="main">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
@ -165,7 +168,7 @@
|
||||
<li><a href="#jobs" id="main_jobs_tab" data-toggle="tab">Jobs</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<div class="tab-content" id="tab-content-container">
|
||||
<div ng-view id="main-view"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user