Merge branch 'devel' into rbac

This commit is contained in:
Wayne Witzel III
2016-02-17 14:26:05 -05:00
22 changed files with 335 additions and 143 deletions

View File

@@ -953,6 +953,7 @@ class InventorySerializer(BaseSerializerWithVariables):
tree = reverse('api:inventory_tree_view', args=(obj.pk,)), tree = reverse('api:inventory_tree_view', args=(obj.pk,)),
inventory_sources = reverse('api:inventory_inventory_sources_list', args=(obj.pk,)), inventory_sources = reverse('api:inventory_inventory_sources_list', args=(obj.pk,)),
activity_stream = reverse('api:inventory_activity_stream_list', args=(obj.pk,)), activity_stream = reverse('api:inventory_activity_stream_list', args=(obj.pk,)),
job_templates = reverse('api:inventory_job_template_list', args=(obj.pk,)),
scan_job_templates = reverse('api:inventory_scan_job_template_list', args=(obj.pk,)), scan_job_templates = reverse('api:inventory_scan_job_template_list', args=(obj.pk,)),
ad_hoc_commands = reverse('api:inventory_ad_hoc_commands_list', args=(obj.pk,)), ad_hoc_commands = reverse('api:inventory_ad_hoc_commands_list', args=(obj.pk,)),
#single_fact = reverse('api:inventory_single_fact_view', args=(obj.pk,)), #single_fact = reverse('api:inventory_single_fact_view', args=(obj.pk,)),

View File

@@ -73,6 +73,7 @@ inventory_urls = patterns('awx.api.views',
url(r'^(?P<pk>[0-9]+)/tree/$', 'inventory_tree_view'), url(r'^(?P<pk>[0-9]+)/tree/$', 'inventory_tree_view'),
url(r'^(?P<pk>[0-9]+)/inventory_sources/$', 'inventory_inventory_sources_list'), url(r'^(?P<pk>[0-9]+)/inventory_sources/$', 'inventory_inventory_sources_list'),
url(r'^(?P<pk>[0-9]+)/activity_stream/$', 'inventory_activity_stream_list'), url(r'^(?P<pk>[0-9]+)/activity_stream/$', 'inventory_activity_stream_list'),
url(r'^(?P<pk>[0-9]+)/job_templates/$', 'inventory_job_template_list'),
url(r'^(?P<pk>[0-9]+)/scan_job_templates/$', 'inventory_scan_job_template_list'), url(r'^(?P<pk>[0-9]+)/scan_job_templates/$', 'inventory_scan_job_template_list'),
url(r'^(?P<pk>[0-9]+)/ad_hoc_commands/$', 'inventory_ad_hoc_commands_list'), url(r'^(?P<pk>[0-9]+)/ad_hoc_commands/$', 'inventory_ad_hoc_commands_list'),
#url(r'^(?P<pk>[0-9]+)/single_fact/$', 'inventory_single_fact_view'), #url(r'^(?P<pk>[0-9]+)/single_fact/$', 'inventory_single_fact_view'),

View File

@@ -1111,6 +1111,20 @@ class InventoryActivityStreamList(SubListAPIView):
qs = self.request.user.get_queryset(self.model) qs = self.request.user.get_queryset(self.model)
return qs.filter(Q(inventory=parent) | Q(host__in=parent.hosts.all()) | Q(group__in=parent.groups.all())) return qs.filter(Q(inventory=parent) | Q(host__in=parent.hosts.all()) | Q(group__in=parent.groups.all()))
class InventoryJobTemplateList(SubListAPIView):
model = JobTemplate
serializer_class = JobTemplateSerializer
parent_model = Inventory
relationship = 'jobtemplates'
new_in_300 = True
def get_queryset(self):
parent = self.get_parent_object()
self.check_parent_access(parent)
qs = self.request.user.get_queryset(self.model)
return qs.filter(inventory=parent)
class InventoryScanJobTemplateList(SubListAPIView): class InventoryScanJobTemplateList(SubListAPIView):
model = JobTemplate model = JobTemplate

View File

@@ -43,6 +43,7 @@
@import "portal.less"; @import "portal.less";
@import "text-label.less"; @import "text-label.less";
@import "./bootstrap-datepicker.less"; @import "./bootstrap-datepicker.less";
@import "awx/ui/client/src/shared/branding/colors.default.less";
/* Bootstrap fix that's causing a right margin to appear /* Bootstrap fix that's causing a right margin to appear
whenver a modal is opened */ whenver a modal is opened */
@@ -107,7 +108,7 @@ a:focus {
} }
.btn-grey:hover { .btn-grey:hover {
background-color: #FFF; background-color: @default-bg;
} }
#cowsay { #cowsay {
@@ -317,7 +318,7 @@ i:active,
#home_groups_table .actions .cancel { padding-right: 3px; } #home_groups_table .actions .cancel { padding-right: 3px; }
.success-badge { .success-badge {
color: #ffffff; color: @default-bg;
background-color: #5cb85c; background-color: #5cb85c;
} }
@@ -630,6 +631,14 @@ dd {
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 5px rgba(255, 88, 80, 0.6); box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 5px rgba(255, 88, 80, 0.6);
} }
.form-control.ng-dirty.ng-pristine {
border-color: @default-second-border;
box-shadow: none;
}
.form-control.ng-dirty.ng-pristine:focus {
border-color: @default-link;
}
/* For some reason TB 3 RC1 does not provide an input-mini */ /* For some reason TB 3 RC1 does not provide an input-mini */
.input-mini { .input-mini {
@@ -704,7 +713,7 @@ legend {
padding-bottom: 0; padding-bottom: 0;
} }
.pagination > .active > a { .pagination > .active > a {
background-color: #fff; background-color: @default-bg;
color: #428bca; color: #428bca;
border-color: none; border-color: none;
border: 1px solid #428bca; border: 1px solid #428bca;
@@ -937,7 +946,7 @@ input[type="checkbox"].checkbox-no-label {
.table-hover tbody tr:hover > td, .table-hover tbody tr:hover > td,
.table-hover tbody tr:hover > th { .table-hover tbody tr:hover > th {
background-color: #fff; background-color: @default-bg;
} }
.table-hover-inverse tbody tr:hover > td, .table-hover-inverse tbody tr:hover > td,
@@ -1161,12 +1170,12 @@ input[type="checkbox"].checkbox-no-label {
/* Inventory job status badge */ /* Inventory job status badge */
.failures-true { .failures-true {
background-color: @red; background-color: @red;
color: #fff; color: @default-bg;
} }
.failures-false { .failures-false {
background-color: @green; background-color: @green;
color: #fff; color: @default-bg;
} }
/* Cloud inventory status. i.e. inventory_source.status values */ /* Cloud inventory status. i.e. inventory_source.status values */
@@ -1349,8 +1358,8 @@ input[type="checkbox"].checkbox-no-label {
} }
.free-button { .free-button {
background-color: #ff5850; background-color: @default-err;
border: 1px solid #ff5850; border: 1px solid @default-err;
color: @white; color: @white;
} }
.free-button:hover { .free-button:hover {
@@ -1939,20 +1948,20 @@ button.dropdown-toggle,
} }
.tooltip.bottom .tooltip-arrow { .tooltip.bottom .tooltip-arrow {
border-bottom-color: #848992; border-bottom-color: @default-interface-txt;
} }
.tooltip.top .tooltip-arrow { .tooltip.top .tooltip-arrow {
border-top-color: #848992; border-top-color: @default-interface-txt;
} }
.tooltip.left .tooltip-arrow { .tooltip.left .tooltip-arrow {
border-left-color: #848992; border-left-color: @default-interface-txt;
} }
.tooltip.right .tooltip-arrow { .tooltip.right .tooltip-arrow {
border-right-color: #848992; border-right-color: @default-interface-txt;
} }
.tooltip.Tooltip.fade.bottom.in { .tooltip.Tooltip.fade.bottom.in {
@@ -1965,7 +1974,7 @@ button.dropdown-toggle,
} }
.tooltip-inner { .tooltip-inner {
background-color: #848992; background-color: @default-interface-txt;
} }
.tooltip-inner--logOut { .tooltip-inner--logOut {
@@ -1986,9 +1995,9 @@ button.dropdown-toggle,
} }
.form-control { .form-control {
border-color: #e1e1e1; border-color: @default-second-border;
background-color: #f6f6f6; background-color: #f6f6f6;
color: #161b1f; color: @default-data-txt;
transition: border-color 0.3s; transition: border-color 0.3s;
box-shadow: none; box-shadow: none;
} }
@@ -2018,3 +2027,11 @@ button.dropdown-toggle,
.list-actions button, .list-actions .checkbox-inline { .list-actions button, .list-actions .checkbox-inline {
margin-top: 10px; margin-top: 10px;
} }
.select2-container--disabled,.select2-container--disabled .select2-selection--single{
cursor: not-allowed !important;
}
.select2-container--disabled {
opacity: .35;
}

View File

@@ -191,8 +191,13 @@
cursor: pointer!important; cursor: pointer!important;
} }
.Form-inputButton {
border-color: @default-second-border;
color: @default-data-txt;
}
.Form-numberInputButton{ .Form-numberInputButton{
color: @field-label!important; color: @default-icon!important;
font-size: 14px; font-size: 14px;
} }

View File

@@ -108,6 +108,8 @@ angular.module('AngularScheduler', ['underscore'])
scope.schedulerEnd = scope.endOptions[0]; scope.schedulerEnd = scope.endOptions[0];
} }
scope.sheduler_frequency_error = false; scope.sheduler_frequency_error = false;
scope.$emit("updateSchedulerSelects");
}; };
scope.showCalendar = function(fld) { scope.showCalendar = function(fld) {
@@ -994,7 +996,11 @@ angular.module('AngularScheduler', ['underscore'])
.filter('schZeroPad', [ function() { .filter('schZeroPad', [ function() {
return function (n, pad) { return function (n, pad) {
var str = (Math.pow(10,pad) + '').replace(/^1/,'') + (n + '').trim(); var str = (Math.pow(10,pad) + '').replace(/^1/,'') + (n + '').trim();
return str.substr(str.length - pad); if (str.substr(str.length - pad) === 'll') {
return undefined;
} else {
return str.substr(str.length - pad);
}
}; };
}]) }])
@@ -1050,6 +1056,10 @@ angular.module('AngularScheduler', ['underscore'])
return { return {
require: 'ngModel', require: 'ngModel',
link: function(scope, element, attr, ctrl) { link: function(scope, element, attr, ctrl) {
if (element.attr("ng-model").indexOf("$parent") > -1) {
scope = scope.$parent;
attr.ngModel = attr.ngModel.split("$parent.")[1];
}
// Add jquerui spinner to 'spinner' type input // Add jquerui spinner to 'spinner' type input
var form = attr.schSpinner, var form = attr.schSpinner,
zeroPad = attr.zeroPad, zeroPad = attr.zeroPad,
@@ -1074,16 +1084,25 @@ angular.module('AngularScheduler', ['underscore'])
}); });
}, 100); }, 100);
}, },
icons: {
down: "Form-numberInputButton fa fa-angle-down",
up: "Form-numberInputButton fa fa-angle-up"
},
spin: function() { spin: function() {
scope[form].$setDirty(); if (scope[form][attr.ngModel]) {
ctrl.$dirty = true; scope[form][attr.ngModel].$setDirty();
ctrl.$pristine = false; scope[form][attr.ngModel].$dirty = true;
scope[form][attr.ngModel].$pristine = false;
}
if (!scope.$$phase) { if (!scope.$$phase) {
scope.$digest(); scope.$digest();
} }
} }
}); });
$('.ui-icon').text('');
$(".ui-icon").removeClass('ui-icon ui-icon-triangle-1-n ui-icon-triangle-1-s');
$(element).on("click", function () { $(element).on("click", function () {
$(element).select(); $(element).select();
}); });

View File

@@ -613,6 +613,10 @@ var tower = angular.module('Tower', [
data: { data: {
activityStreamId: 'organization_id' activityStreamId: 'organization_id'
}, },
ncyBreadcrumb: {
parent: "organizations",
label: "{{name}}"
},
resolve: { resolve: {
features: ['FeaturesService', function(FeaturesService) { features: ['FeaturesService', function(FeaturesService) {
return FeaturesService.get(); return FeaturesService.get();

View File

@@ -52,6 +52,7 @@
.BreadCrumb-item { .BreadCrumb-item {
display: inline-block; display: inline-block;
color: #B7B7B7; color: #B7B7B7;
text-transform: uppercase;
} }
.BreadCrumb-item + .BreadCrumb-item:before { .BreadCrumb-item + .BreadCrumb-item:before {

View File

@@ -12,13 +12,38 @@
export function OrganizationsList($stateParams, $scope, $rootScope, $location, export function OrganizationsList($stateParams, $scope, $rootScope, $location,
$log, Rest, Alert, Prompt, ClearScope, ProcessErrors, GetBasePath, Wait, $log, $compile, Rest, PaginateWidget, PaginateInit, SearchInit, OrganizationList, Alert, Prompt, ClearScope, ProcessErrors, GetBasePath, Wait,
$state) { $state) {
ClearScope(); ClearScope();
var defaultUrl = GetBasePath('organizations'); var defaultUrl = GetBasePath('organizations'),
list = OrganizationList,
pageSize = $scope.orgCount;
PaginateInit({
scope: $scope,
list: list,
url: defaultUrl,
pageSize: pageSize,
});
SearchInit({
scope: $scope,
list: list,
url: defaultUrl,
});
$scope.search(list.iterator);
$scope.PaginateWidget = PaginateWidget({
iterator: list.iterator,
set: 'organizations'
});
var paginationContainer = $('#pagination-container');
paginationContainer.html($scope.PaginateWidget);
$compile(paginationContainer.contents())($scope)
var parseCardData = function (cards) { var parseCardData = function (cards) {
return cards.map(function (card) { return cards.map(function (card) {
var val = {}; var val = {};
@@ -150,7 +175,7 @@ export function OrganizationsList($stateParams, $scope, $rootScope, $location,
} }
OrganizationsList.$inject = ['$stateParams', '$scope', '$rootScope', OrganizationsList.$inject = ['$stateParams', '$scope', '$rootScope',
'$location', '$log', 'Rest', 'Alert', 'Prompt', 'ClearScope', '$location', '$log', '$compile', 'Rest', 'PaginateWidget', 'PaginateInit', 'SearchInit', 'OrganizationList', 'Alert', 'Prompt', 'ClearScope',
'ProcessErrors', 'GetBasePath', 'Wait', 'ProcessErrors', 'GetBasePath', 'Wait',
'$state' '$state'
]; ];
@@ -354,4 +379,4 @@ OrganizationsEdit.$inject = ['$scope', '$rootScope', '$compile', '$location',
'$log', '$stateParams', 'OrganizationForm', 'GenerateForm', 'Rest', 'Alert', '$log', '$stateParams', 'OrganizationForm', 'GenerateForm', 'Rest', 'Alert',
'ProcessErrors', 'RelatedSearchInit', 'RelatedPaginateInit', 'Prompt', 'ProcessErrors', 'RelatedSearchInit', 'RelatedPaginateInit', 'Prompt',
'ClearScope', 'GetBasePath', 'Wait', '$state' 'ClearScope', 'GetBasePath', 'Wait', '$state'
]; ];

View File

@@ -55,9 +55,9 @@ export default
}]) }])
.factory('EditSchedule', ['SchedulerInit', 'ShowSchedulerModal', 'Wait', .factory('EditSchedule', ['SchedulerInit', 'ShowSchedulerModal', 'Wait',
'Rest', 'ProcessErrors', 'GetBasePath', 'SchedulePost', 'Rest', 'ProcessErrors', 'GetBasePath', 'SchedulePost', '$state',
function(SchedulerInit, ShowSchedulerModal, Wait, Rest, ProcessErrors, function(SchedulerInit, ShowSchedulerModal, Wait, Rest, ProcessErrors,
GetBasePath, SchedulePost) { GetBasePath, SchedulePost, $state) {
return function(params) { return function(params) {
var scope = params.scope, var scope = params.scope,
id = params.id, id = params.id,
@@ -171,6 +171,7 @@ export default
if (callback) { if (callback) {
scope.$emit(callback, data); scope.$emit(callback, data);
} }
$state.go("^");
}); });
scope.saveSchedule = function() { scope.saveSchedule = function() {
@@ -208,8 +209,8 @@ export default
}]) }])
.factory('AddSchedule', ['$location', '$stateParams', 'SchedulerInit', 'ShowSchedulerModal', 'Wait', 'GetBasePath', 'Empty', .factory('AddSchedule', ['$location', '$stateParams', 'SchedulerInit', 'ShowSchedulerModal', 'Wait', 'GetBasePath', 'Empty',
'SchedulePost', 'SchedulePost', '$state',
function($location, $stateParams, SchedulerInit, ShowSchedulerModal, Wait, GetBasePath, Empty, SchedulePost) { function($location, $stateParams, SchedulerInit, ShowSchedulerModal, Wait, GetBasePath, Empty, SchedulePost, $state) {
return function(params) { return function(params) {
var scope = params.scope, var scope = params.scope,
callback= params.callback, callback= params.callback,
@@ -280,6 +281,7 @@ export default
if (callback) { if (callback) {
scope.$emit(callback, data); scope.$emit(callback, data);
} }
$state.go("^");
}); });
scope.saveSchedule = function() { scope.saveSchedule = function() {

View File

@@ -57,4 +57,6 @@
</div> </div>
</div> </div>
</div> </div>
</div> <div id="pagination-container" ng-hide="organization_num_pages < 2">
</div>
</div>

View File

@@ -1,9 +1,11 @@
/** @define RepeatFrequencyOptions */ /** @define RepeatFrequencyOptions */
@import "awx/ui/client/src/shared/branding/colors.default.less";
.RepeatFrequencyOptions { .RepeatFrequencyOptions {
width: ~"calc(100% + 21px)"; width: ~"calc(100% + 21px)";
padding: 20px; padding: 20px;
border-left: 5px solid #e8e8e8; border-left: 5px solid @default-border;
margin-left: -20px; margin-left: -20px;
padding-left: 17px; padding-left: 17px;
padding-right: 0px; padding-right: 0px;
@@ -60,10 +62,10 @@
margin-top: -2px; margin-top: -2px;
text-transform: uppercase; text-transform: uppercase;
font-weight: bold; font-weight: bold;
color: #848992; color: @default-interface-txt;
font-size: 13px; font-size: 13px;
margin-left: -20px; margin-left: -20px;
border-left: 5px solid #e8e8e8; border-left: 5px solid @default-border;
padding-left: 15px; padding-left: 15px;
padding-bottom: 15px; padding-bottom: 15px;
} }
@@ -94,7 +96,7 @@
.RepeatFrequencyOptions-inlineLabel { .RepeatFrequencyOptions-inlineLabel {
font-weight: normal; font-weight: normal;
color: #848992; color: @default-interface-txt;
text-transform: uppercase; text-transform: uppercase;
flex: initial; flex: initial;
width: 54px; width: 54px;
@@ -115,14 +117,15 @@
margin-top: 2px; margin-top: 2px;
} }
.RepeatFrequencyOptions-spacedSelect { .RepeatFrequencyOptions-spacedSelect,
.RepeatFrequencyOptions-spacedSelect ~ .select2 {
margin-bottom: 10px; margin-bottom: 10px;
} }
.RepeatFrequencyOptions-subFormBorderFixer { .RepeatFrequencyOptions-subFormBorderFixer {
height: 25px; height: 25px;
width: 5px; width: 5px;
background: #ffffff; background: @default-bg;
margin-left: -20px; margin-left: -20px;
margin-top: -25px; margin-top: -25px;
margin-right: 50px; margin-right: 50px;
@@ -137,3 +140,25 @@
flex: initial; flex: initial;
width: 100%; width: 100%;
} }
.RepeatFrequencyOptions-nameBorderErrorFix {
border-color: @default-err !important;
}
.RepeatFrequencyOptions-inputGroup {
display: flex;
justify-content: space-between;
}
.RepeatFrequencyOptions-inputGroup--thirds > .select2 {
width: ~"calc(33% - 3px)" !important;
}
.RepeatFrequencyOptions-inputGroup--halves > .select2 {
width: ~"calc(50% - 3px)" !important;
}
.RepeatFrequencyOptions-inputGroup--halvesWithNumber > .select2 {
width: ~"calc(50% - 3px)" !important;
margin-right: 7px;
}

View File

@@ -1,19 +1,21 @@
/** @define ScheduleToggle */ /** @define ScheduleToggle */
@import "awx/ui/client/src/shared/branding/colors.default.less";
.ScheduleToggle { .ScheduleToggle {
border-radius: 5px; border-radius: 5px;
border: 1px solid #b7b7b7; border: 1px solid @default-icon;
background-color: #b7b7b7; background-color: @default-icon;
width: 40px; width: 40px;
margin-top: 2px; margin-top: 2px;
cursor: pointer; cursor: pointer;
} }
.ScheduleToggle-switch { .ScheduleToggle-switch {
color: #848992; color: @default-interface-txt;
background-color: #fff; background-color: @default-bg;
margin-left: 4px; margin-left: 4px;
border-left: 1px solid #b7b7b7; border-left: 1px solid @default-icon;
margin-right: 0px; margin-right: 0px;
text-align: center; text-align: center;
text-transform: uppercase; text-transform: uppercase;
@@ -30,8 +32,8 @@
.ScheduleToggle-switch.is-on { .ScheduleToggle-switch.is-on {
margin-right: 5px; margin-right: 5px;
margin-left: 0px; margin-left: 0px;
background-color: #1678c4; background-color: @default-link;
color: #fff; color: @default-bg;
border-left: 0; border-left: 0;
border-top-right-radius: 0px; border-top-right-radius: 0px;
border-bottom-right-radius: 0px; border-bottom-right-radius: 0px;
@@ -40,13 +42,13 @@
} }
.ScheduleToggle-switch:hover { .ScheduleToggle-switch:hover {
background-color: #fafafa; background-color: @default-tertiary-bg;
} }
.ScheduleToggle.is-on:hover { .ScheduleToggle.is-on:hover {
border-color: #4498DA; border-color: @default-link-hov;
} }
.ScheduleToggle-switch.is-on:hover { .ScheduleToggle-switch.is-on:hover {
background-color: #4498DA; background-color: @default-link-hov;
} }

View File

@@ -1,4 +1,4 @@
export default ['$compile', '$state', '$stateParams', 'AddSchedule', 'Wait', '$scope', '$rootScope', function($compile, $state, $stateParams, AddSchedule, Wait, $scope, $rootScope) { export default ['$compile', '$state', '$stateParams', 'AddSchedule', 'Wait', '$scope', '$rootScope', 'CreateSelect2', function($compile, $state, $stateParams, AddSchedule, Wait, $scope, $rootScope, CreateSelect2) {
$scope.$on("ScheduleFormCreated", function(e, scope) { $scope.$on("ScheduleFormCreated", function(e, scope) {
$scope.hideForm = false; $scope.hideForm = false;
$scope = angular.extend($scope, scope); $scope = angular.extend($scope, scope);
@@ -43,10 +43,23 @@ export default ['$compile', '$state', '$stateParams', 'AddSchedule', 'Wait', '$s
$scope.formCancel = function() { $scope.formCancel = function() {
$state.go("^"); $state.go("^");
} };
AddSchedule({ AddSchedule({
scope: $scope, scope: $scope,
callback: 'SchedulesRefresh' callback: 'SchedulesRefresh'
}); });
var callSelect2 = function() {
CreateSelect2({
element: '.MakeSelect2',
multiple: false
});
};
$scope.$on("updateSchedulerSelects", function() {
callSelect2();
});
callSelect2();
}]; }];

View File

@@ -1,4 +1,4 @@
export default ['$compile', '$state', '$stateParams', 'EditSchedule', 'Wait', '$scope', '$rootScope', function($compile, $state, $stateParams, EditSchedule, Wait, $scope, $rootScope) { export default ['$compile', '$state', '$stateParams', 'EditSchedule', 'Wait', '$scope', '$rootScope', 'CreateSelect2', function($compile, $state, $stateParams, EditSchedule, Wait, $scope, $rootScope, CreateSelect2) {
$scope.$on("ScheduleFormCreated", function(e, scope) { $scope.$on("ScheduleFormCreated", function(e, scope) {
$scope.hideForm = false; $scope.hideForm = false;
$scope = angular.extend($scope, scope); $scope = angular.extend($scope, scope);
@@ -53,4 +53,17 @@ export default ['$compile', '$state', '$stateParams', 'EditSchedule', 'Wait', '$
id: parseInt($stateParams.schedule_id), id: parseInt($stateParams.schedule_id),
callback: 'SchedulesRefresh' callback: 'SchedulesRefresh'
}); });
var callSelect2 = function() {
CreateSelect2({
element: '.MakeSelect2',
multiple: false
});
};
$scope.$on("updateSchedulerSelects", function() {
callSelect2();
});
callSelect2();
}]; }];

View File

@@ -1,5 +1,7 @@
/** @define SchedulerForm */ /** @define SchedulerForm */
@import "awx/ui/client/src/shared/branding/colors.default.less";
.SchedulerForm-formGroup { .SchedulerForm-formGroup {
padding-right: 0px; padding-right: 0px;
} }

View File

@@ -12,7 +12,7 @@
<form class="form Form" <form class="form Form"
role="form" role="form"
name="scheduler_form" name="scheduler_form_new"
novalidate> novalidate>
<div class="form-group SchedulerForm-formGroup"> <div class="form-group SchedulerForm-formGroup">
@@ -22,13 +22,15 @@
</label> </label>
<input <input
type="text" type="text"
class="form-control input-sm" class="form-control input-sm
Form-textInput"
ng-class="{'RepeatFrequencyOptions-nameBorderErrorFix': scheduler_form_new.$dirty && scheduler_form_new.schedulerName.$error.required}"
name="schedulerName" name="schedulerName"
id="schedulerName" id="schedulerName"
ng-model="schedulerName" required ng-model="schedulerName" required
placeholder="Schedule name"> placeholder="Schedule name">
<div class="error" <div class="error"
ng-show="scheduler_form.schedulerName.$dirty && scheduler_form.schedulerName.$error.required"> ng-show="scheduler_form_new.$dirty && scheduler_form_new.schedulerName.$error.required">
A schedule name is required. A schedule name is required.
</div> </div>
</div> </div>
@@ -40,9 +42,10 @@
(mm/dd/yyyy) (mm/dd/yyyy)
</span> </span>
</label> </label>
<div class="input-group"> <div class="input-group Form-inputGroup">
<input type="text" <input type="text"
class="form-control input-sm" class="form-control input-sm
Form-textInput"
name="schedulerStartDt" name="schedulerStartDt"
id="schedulerStartDt" id="schedulerStartDt"
ng-model="schedulerStartDt" ng-model="schedulerStartDt"
@@ -52,7 +55,8 @@
ng-change="scheduleTimeChange()" > ng-change="scheduleTimeChange()" >
<span class="input-group-btn"> <span class="input-group-btn">
<button <button
class="btn btn-default btn-sm" class="btn btn-default btn-sm
Form-inputButton Form-lookupButton"
type="button" type="button"
ng-click="showCalendar('schedulerStartDt')"> ng-click="showCalendar('schedulerStartDt')">
<i class="fa fa-calendar"></i> <i class="fa fa-calendar"></i>
@@ -80,7 +84,7 @@
<div class="input-group SchedulerTime"> <div class="input-group SchedulerTime">
<input name="schedulerStartHour" <input name="schedulerStartHour"
id="schedulerStartHour" id="schedulerStartHour"
sch-spinner="scheduler_form" sch-spinner="scheduler_form_new"
class="scheduler-time-spinner class="scheduler-time-spinner
ScheduleTime-input SpinnerInput" ScheduleTime-input SpinnerInput"
ng-model="schedulerStartHour" ng-model="schedulerStartHour"
@@ -94,7 +98,7 @@
</span> </span>
<input name="schedulerStartMinute" <input name="schedulerStartMinute"
id="schedulerStartMinute" id="schedulerStartMinute"
sch-spinner="scheduler_form" sch-spinner="scheduler_form_new"
class="scheduler-time-spinner class="scheduler-time-spinner
SchedulerTime-input SpinnerInput" SchedulerTime-input SpinnerInput"
ng-model="schedulerStartMinute" ng-model="schedulerStartMinute"
@@ -108,7 +112,7 @@
</span> </span>
<input name="schedulerStartSecond" <input name="schedulerStartSecond"
id="schedulerStartSecond" id="schedulerStartSecond"
sch-spinner="scheduler_form" sch-spinner="scheduler_form_new"
class="scheduler-time-spinner class="scheduler-time-spinner
SchedulerTime-input SpinnerInput" SchedulerTime-input SpinnerInput"
ng-model="schedulerStartSecond" ng-model="schedulerStartSecond"
@@ -137,9 +141,11 @@
<div class="form-group SchedulerForm-formGroup" <div class="form-group SchedulerForm-formGroup"
ng-show="schedulerShowTimeZone"> ng-show="schedulerShowTimeZone">
<label class="Form-inputLabel"> <label class="Form-inputLabel">
<span class="red-text">*</span>
Local Time Zone Local Time Zone
</label> </label>
<select <select
class="MakeSelect2"
name="schedulerTimeZone" name="schedulerTimeZone"
id="schedulerTimeZone" id="schedulerTimeZone"
ng-model="schedulerTimeZone" ng-model="schedulerTimeZone"
@@ -150,10 +156,12 @@
</div> </div>
<div class="form-group SchedulerForm-formGroup"> <div class="form-group SchedulerForm-formGroup">
<label class="Form-inputLabel"> <label class="Form-inputLabel">
<span class="red-text">*</span>
Repeat frequency Repeat frequency
</label> </label>
<select name="schedulerFrequency" <select name="schedulerFrequency"
id="schedulerFrequency" id="schedulerFrequency"
class="MakeSelect2"
ng-model="schedulerFrequency" ng-model="schedulerFrequency"
ng-options="f.name for f in frequencyOptions" ng-options="f.name for f in frequencyOptions"
required class="form-control input-sm" required class="form-control input-sm"
@@ -174,11 +182,12 @@
ng-if="schedulerShowInterval"> ng-if="schedulerShowInterval">
<label class="Form-inputLabel <label class="Form-inputLabel
RepeatFrequencyOptions-everyLabel"> RepeatFrequencyOptions-everyLabel">
<span class="red-text">*</span>
Every Every
</label> </label>
<input name="schedulerInterval" <input name="schedulerInterval"
id="schedulerInterval" id="schedulerInterval"
sch-spinner="scheduler_form" sch-spinner="scheduler_form_new"
class="scheduler-spinner class="scheduler-spinner
SpinnerInput" SpinnerInput"
ng-model="$parent.schedulerInterval" ng-model="$parent.schedulerInterval"
@@ -202,6 +211,7 @@
<div class="radio <div class="radio
RepeatFrequencyOptions-radioLabel"> RepeatFrequencyOptions-radioLabel">
<label class="Form-inputLabel"> <label class="Form-inputLabel">
<span class="red-text">*</span>
<input type="radio" value="day" <input type="radio" value="day"
ng-model="$parent.monthlyRepeatOption" ng-model="$parent.monthlyRepeatOption"
ng-change="monthlyRepeatChange()" ng-change="monthlyRepeatChange()"
@@ -213,7 +223,7 @@
<input <input
name="monthDay" name="monthDay"
id="monthDay" id="monthDay"
sch-spinner="scheduler_form" sch-spinner="scheduler_form_new"
class="scheduler-spinner SpinnerInput" class="scheduler-spinner SpinnerInput"
ng-model="$parent.monthDay" ng-model="$parent.monthDay"
min="1" max="31" min="1" max="31"
@@ -229,6 +239,7 @@
<div class="radio <div class="radio
RepeatFrequencyOptions-radioLabel"> RepeatFrequencyOptions-radioLabel">
<label class="Form-inputLabel"> <label class="Form-inputLabel">
<span class="red-text">*</span>
<input type="radio" <input type="radio"
value="other" value="other"
ng-model="$parent.monthlyRepeatOption" ng-model="$parent.monthlyRepeatOption"
@@ -238,29 +249,34 @@
on the on the
</label> </label>
</div> </div>
<select name="monthlyOccurrence" <div class="RepeatFrequencyOptions-inputGroup
id="monthlyOccurrence" RepeatFrequencyOptions-inputGroup--halves">
ng-model="$parent.monthlyOccurrence" <select name="monthlyOccurrence"
ng-options="o.name for o in occurrences" id="monthlyOccurrence"
ng-disabled="monthlyRepeatOption != 'other'" ng-model="$parent.monthlyOccurrence"
class="form-control input-sm ng-options="o.name for o in occurrences"
RepeatFrequencyOptions-spacedSelect ng-disabled="monthlyRepeatOption != 'other'"
RepeatFrequencyOptions-monthlyOccurence" class=" MakeSelect2 form-control
> input-sm
</select> RepeatFrequencyOptions-spacedSelect
<select name="monthlyWeekDay" RepeatFrequencyOptions-monthlyOccurence"
id="monthlyWeekDay" >
ng-model="$parent.monthlyWeekDay" </select>
ng-options="w.name for w in weekdays" <select name="monthlyWeekDay"
ng-disabled="monthlyRepeatOption != 'other'" id="monthlyWeekDay"
class="form-control input-sm" > ng-model="$parent.monthlyWeekDay"
</select> ng-options="w.name for w in weekdays"
ng-disabled="monthlyRepeatOption != 'other'"
class="MakeSelect2 form-control input-sm" >
</select>
</div>
</div> </div>
<div class="form-group <div class="form-group
RepeatFrequencyOptions-formGroup" RepeatFrequencyOptions-formGroup"
ng-if="schedulerFrequency && schedulerFrequency.value == 'yearly'"> ng-if="schedulerFrequency && schedulerFrequency.value == 'yearly'">
<div class="radio <div class="radio
RepeatFrequencyOptions-radioLabel"> RepeatFrequencyOptions-radioLabel">
<span class="red-text">*</span>
<label class="Form-inputLabel"> <label class="Form-inputLabel">
<input type="radio" <input type="radio"
value="month" value="month"
@@ -271,24 +287,27 @@
on on
</label> </label>
</div> </div>
<select name="yearlyMonth" <div class="RepeatFrequencyOptions-inputGroup
id="yearlyMonth" RepeatFrequencyOptions-inputGroup--halvesWithNumber">
ng-model="$parent.yearlyMonth" <select name="yearlyMonth"
ng-options="m.name for m in months" id="yearlyMonth"
ng-disabled="yearlyRepeatOption != 'month'" ng-model="$parent.yearlyMonth"
class="form-control input-sm ng-options="m.name for m in months"
RepeatFrequencyOptions-spacedSelect" ng-disabled="yearlyRepeatOption != 'month'"
> class="MakeSelect2 form-control input-sm
</select> RepeatFrequencyOptions-spacedSelect"
<input name="yearlyMonthDay" >
id="yearlyMonthDay" </select>
sch-spinner="scheduler_form" <input name="yearlyMonthDay"
class="scheduler-spinner id="yearlyMonthDay"
SpinnerInput" sch-spinner="scheduler_form_new"
ng-model="$parent.yearlyMonthDay" class="scheduler-spinner
min="1" max="31" SpinnerInput"
ng-change="resetError('scheduler_yearlyMonthDay_error')" ng-model="$parent.yearlyMonthDay"
> min="1" max="31"
ng-change="resetError('scheduler_yearlyMonthDay_error')"
>
</div>
<div class="error" <div class="error"
ng-show="$parent.scheduler_yearlyMonthDay_error"> ng-show="$parent.scheduler_yearlyMonthDay_error">
The day must be between 1 and 31. The day must be between 1 and 31.
@@ -300,6 +319,7 @@
<div class="radio <div class="radio
RepeatFrequencyOptions-radioLabel"> RepeatFrequencyOptions-radioLabel">
<label class="Form-inputLabel"> <label class="Form-inputLabel">
<span class="red-text">*</span>
<input type="radio" <input type="radio"
value="other" value="other"
ng-model="$parent.yearlyRepeatOption" ng-model="$parent.yearlyRepeatOption"
@@ -309,32 +329,43 @@
on the on the
</label> </label>
</div> </div>
<select name="yearlyOccurrence" <div
id="yearlyOccurrence" class="RepeatFrequencyOptions-inputGroup
ng-model="$parent.yearlyOccurrence" RepeatFrequencyOptions-inputGroup--thirds"
ng-options="o.name for o in occurrences"
ng-disabled="yearlyRepeatOption != 'other'"
class="form-control input-sm
RepeatFrequencyOptions-spacedSelect
RepeatFrequencyOptions-yearlyOccurence"
> >
</select> <select name="yearlyOccurrence"
<select name="yearlyWeekDay" id="yearlyOccurrence"
id="yearlyWeekDay" ng-model="$parent.yearlyOccurrence"
ng-model="$parent.yearlyWeekDay" ng-options="o.name for o in occurrences"
ng-options="w.name for w in weekdays" ng-disabled="yearlyRepeatOption != 'other'"
ng-disabled="yearlyRepeatOption != 'other'" class="MakeSelect2
class="form-control input-sm form-control input-sm
RepeatFrequencyOptions-spacedSelect" RepeatFrequencyOptions-spacedSelect
> RepeatFrequencyOptions-yearlyOccurence
</select> RepeatFrequencyOptions-thirdSelect"
<select name="yearlyOtherMonth" >
id="yearlyOtherMonth" </select>
ng-model="$parent.yearlyOtherMonth" <select name="yearlyWeekDay"
ng-options="m.name for m in months" id="yearlyWeekDay"
ng-disabled="yearlyRepeatOption != 'other'" ng-model="$parent.yearlyWeekDay"
class="form-control input-sm"> ng-options="w.name for w in weekdays"
</select> ng-disabled="yearlyRepeatOption != 'other'"
class="MakeSelect2
form-control input-sm
RepeatFrequencyOptions-spacedSelect
RepeatFrequencyOptions-thirdSelect"
>
</select>
<select name="yearlyOtherMonth"
id="yearlyOtherMonth"
ng-model="$parent.yearlyOtherMonth"
ng-options="m.name for m in months"
ng-disabled="yearlyRepeatOption != 'other'"
class="MakeSelect2
form-control input-sm
RepeatFrequencyOptions-thirdSelect">
</select>
</div>
</div> </div>
<div class="form-group <div class="form-group
RepeatFrequencyOptions-week RepeatFrequencyOptions-week
@@ -417,6 +448,7 @@
RepeatFrequencyOptions-formGroup" RepeatFrequencyOptions-formGroup"
ng-if="schedulerShowInterval"> ng-if="schedulerShowInterval">
<label class="Form-inputLabel"> <label class="Form-inputLabel">
<span class="red-text">*</span>
End End
</label> </label>
<div> <div>
@@ -425,7 +457,8 @@
ng-model="$parent.schedulerEnd" ng-model="$parent.schedulerEnd"
ng-options="e.name for e in endOptions" ng-options="e.name for e in endOptions"
required required
class="form-control input-sm" class="MakeSelect2
form-control input-sm"
ng-change="schedulerEndChange()"> ng-change="schedulerEndChange()">
</select> </select>
</div> </div>
@@ -435,12 +468,13 @@
RepeatFrequencyOptions-formGroup" RepeatFrequencyOptions-formGroup"
ng-if="schedulerEnd && schedulerEnd.value == 'after'"> ng-if="schedulerEnd && schedulerEnd.value == 'after'">
<label class="Form-inputLabel"> <label class="Form-inputLabel">
<span class="red-text">*</span>
Occurrence(s) Occurrence(s)
</label> </label>
<input <input
ng-name="schedulerOccurrenceCount" ng-name="schedulerOccurrenceCount"
ng-id="schedulerOccurrenceCount" ng-id="schedulerOccurrenceCount"
sch-spinner="scheduler_form" sch-spinner="scheduler_form_new"
class="scheduler-spinner class="scheduler-spinner
SpinnerInput" SpinnerInput"
ng-model="$parent.schedulerOccurrenceCount" ng-model="$parent.schedulerOccurrenceCount"
@@ -454,7 +488,7 @@
</div> </div>
</div> </div>
<div class="form-group RepeatFrequencyOptions-formGroup" <div class="form-group RepeatFrequencyOptions-formGroup"
ng-show="schedulerEnd && schedulerEnd.value == 'on'"> ng-if="schedulerEnd && schedulerEnd.value == 'on'">
<label class="Form-inputLabel"> <label class="Form-inputLabel">
<span class="red-text">*</span> <span class="red-text">*</span>
End Date End Date
@@ -462,18 +496,20 @@
(mm/dd/yyyy) (mm/dd/yyyy)
</span> </span>
</label> </label>
<div class="input-group"> <div class="input-group Form-inputGroup">
<input type="text" <input type="text"
name="schedulerEndDt" name="schedulerEndDt"
id="schedulerEndDt" id="schedulerEndDt"
class="form-control input-sm" class="form-control input-sm
ng-model="schedulerEndDt" Form-textInput"
ng-model="$parent.schedulerEndDt"
sch-date-picker sch-date-picker
data-min-today="true" data-min-today="true"
placeholder="mm/dd/yyyy" placeholder="mm/dd/yyyy"
ng-change="resetError('scheduler_endDt_error')"> ng-change="$parent.resetError('scheduler_endDt_error')">
<span class="input-group-btn"> <span class="input-group-btn">
<button class="btn btn-default btn-sm" <button class="btn btn-default btn-sm
Form-inputButton Form-lookupButton"
type="button" type="button"
ng-click="showCalendar('schedulerEndDt')" ng-click="showCalendar('schedulerEndDt')"
> >
@@ -534,7 +570,7 @@
</form> </form>
<div class="SchedulerFormDetail-container <div class="SchedulerFormDetail-container
SchedulerFormDetail-container--error" SchedulerFormDetail-container--error"
ng-show="!schedulerIsValid"> ng-show="!schedulerIsValid && scheduler_form_new.$dirty">
<p class="SchedulerFormDetail-errorText"> <p class="SchedulerFormDetail-errorText">
The scheduler options are invalid or incomplete. The scheduler options are invalid or incomplete.
</p> </p>

View File

@@ -1,14 +1,16 @@
/** @define SchedulerFormDetail */ /** @define SchedulerFormDetail */
@import "awx/ui/client/src/shared/branding/colors.default.less";
.SchedulerFormDetail-container { .SchedulerFormDetail-container {
padding: 15px; padding: 15px;
border: 1px solid #e8e8e8; border: 1px solid @default-border;
border-radius: 5px; border-radius: 5px;
margin-bottom: 20px; margin-bottom: 20px;
} }
.SchedulerFormDetail-container--error { .SchedulerFormDetail-container--error {
border-color: #ff5850; border-color: @default-err;
} }
.SchedulerFormDetail-errorText { .SchedulerFormDetail-errorText {
@@ -19,7 +21,7 @@
.SchedulerFormDetail-label { .SchedulerFormDetail-label {
text-transform: uppercase; text-transform: uppercase;
color: #848992; color: @default-interface-txt;
margin-bottom: 15px; margin-bottom: 15px;
} }
@@ -47,7 +49,7 @@
.SchedulerFormDetail-dateFormats { .SchedulerFormDetail-dateFormats {
text-transform: uppercase; text-transform: uppercase;
font-size: 13px; font-size: 13px;
color: #848992; color: @default-interface-txt;
} }
.SchedulerFormDetail-dateFormatsLabel { .SchedulerFormDetail-dateFormatsLabel {
@@ -57,12 +59,12 @@
.SchedulerFormDetail-radioLabel { .SchedulerFormDetail-radioLabel {
margin-top: -3px !important; margin-top: -3px !important;
color: #161B1F !important; color: @default-data-txt !important;
} }
.SchedulerFormDetail-radioButton { .SchedulerFormDetail-radioButton {
margin-top: 2px !important; margin-top: 2px !important;
color: #848992 !important; color: @default-interface-txt !important;
} }
.SchedulerFormDetail-occurrenceList { .SchedulerFormDetail-occurrenceList {

View File

@@ -1,5 +1,7 @@
/** @define SchedulerTime */ /** @define SchedulerTime */
@import "awx/ui/client/src/shared/branding/colors.default.less";
.SchedulerTime { .SchedulerTime {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
@@ -14,7 +16,7 @@ span.ui-spinner.ui-widget.ui-widget-content.ui-corner-all {
.SchedulerTime-separator { .SchedulerTime-separator {
margin-left: 3px; margin-left: 3px;
margin-right: 3px; margin-right: 3px;
margin-top: 2px; margin-top: 3px;
} }
.SchedulerTime-utc { .SchedulerTime-utc {

View File

@@ -1,5 +1,7 @@
/** @define SpinnerInput */ /** @define SpinnerInput */
@import "awx/ui/client/src/shared/branding/colors.default.less";
.SpinnerInput { .SpinnerInput {
width: ~"calc(100% - 26px)"; width: ~"calc(100% - 26px)";
} }

View File

@@ -15,6 +15,10 @@
@default-link: #1678C4; @default-link: #1678C4;
@default-link-hov: #4498DA; @default-link-hov: #4498DA;
@default-button-hov: #F2F2F2; @default-button-hov: #F2F2F2;
@default-list-header-bg:#EBEBEB;
@default-no-items-bord: #F6F6F6;
@default-as-detail-txt: #707070;
@default-dark: #000000;
// layout // layout
@page-bg: @default-secondary-bg; @page-bg: @default-secondary-bg;
@@ -31,7 +35,7 @@
// lists // lists
@list-empty-txt: @default-interface-txt; @list-empty-txt: @default-interface-txt;
@list-header-bord: @default-bg; @list-header-bord: @default-bg;
@list-header-bg: #EBEBEB; @list-header-bg: @default-list-header-bg;
@list-header-txt: @default-interface-txt; @list-header-txt: @default-interface-txt;
@list-header-icon: @default-icon; @list-header-icon: @default-icon;
@list-item: @default-data-txt; @list-item: @default-data-txt;
@@ -64,7 +68,7 @@
@list-srch-btn-bg: @default-bg; @list-srch-btn-bg: @default-bg;
@list-srch-btn-hov-bg: @default-tertiary-bg; @list-srch-btn-hov-bg: @default-tertiary-bg;
@list-no-items-txt: @default-icon; @list-no-items-txt: @default-icon;
@list-no-items-bord: #F6F6F6; @list-no-items-bord: @default-no-items-bord;
@list-no-items-bg: @default-secondary-bg; @list-no-items-bg: @default-secondary-bg;
// tooltups // tooltups
@@ -73,7 +77,7 @@
// login modal // login modal
@login-alert: @default-interface-txt; @login-alert: @default-interface-txt;
@login-backdrop: #000000; @login-backdrop: @default-dark;
@login-bg: @default-bg; @login-bg: @default-bg;
@login-header-bg: @default-bg; @login-header-bg: @default-bg;
@login-alert-error: @default-err; @login-alert-error: @default-err;
@@ -82,7 +86,7 @@
@login-notice-title: @default-interface-txt; @login-notice-title: @default-interface-txt;
@login-notice-bg: @default-secondary-bg; @login-notice-bg: @default-secondary-bg;
@login-notice-border: @default-secondary-bg; @login-notice-border: @default-secondary-bg;
@login-notice-text: #707070; @login-notice-text: @default-as-detail-txt;
// login modal third party auth // login modal third party auth
@third-party-label: @default-interface-txt; @third-party-label: @default-interface-txt;
@@ -157,8 +161,8 @@
@db-graph-axis-label: @default-interface-txt; @db-graph-axis-label: @default-interface-txt;
// panel // panel
@panel-bg: @default-bg; @panel-bg: @default-bg;
@panel-border: @default-border; @panel-border: @default-border;
// activity stream details modal // activity stream details modal
@as-detail-changes-txt: #707070; @as-detail-changes-txt: @default-as-detail-txt;

View File

@@ -5,7 +5,7 @@ ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8 ENV LC_ALL en_US.UTF-8
RUN apt-get update && apt-get install -y software-properties-common python-software-properties curl RUN apt-get update && apt-get install -y software-properties-common python-software-properties curl
RUN add-apt-repository -y ppa:chris-lea/redis-server; add-apt-repository -y ppa:chris-lea/zeromq; add-apt-repository -y ppa:chris-lea/node.js; add-apt-repository ppa:ansible/ansible RUN add-apt-repository -y ppa:chris-lea/redis-server; add-apt-repository -y ppa:chris-lea/zeromq; add-apt-repository -y ppa:chris-lea/node.js; add-apt-repository -y ppa:ansible/ansible
RUN curl -sL https://deb.nodesource.com/setup_0.12 | bash - RUN curl -sL https://deb.nodesource.com/setup_0.12 | bash -
RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10 && apt-key adv --fetch-keys http://www.postgresql.org/media/keys/ACCC4CF8.asc RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10 && apt-key adv --fetch-keys http://www.postgresql.org/media/keys/ACCC4CF8.asc
RUN echo "deb http://repo.mongodb.org/apt/ubuntu "$(lsb_release -sc)"/mongodb-org/3.0 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-3.0.list && echo "deb http://apt.postgresql.org/pub/repos/apt/ trusty-pgdg main" | tee /etc/apt/sources.list.d/postgres-9.4.list RUN echo "deb http://repo.mongodb.org/apt/ubuntu "$(lsb_release -sc)"/mongodb-org/3.0 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-3.0.list && echo "deb http://apt.postgresql.org/pub/repos/apt/ trusty-pgdg main" | tee /etc/apt/sources.list.d/postgres-9.4.list