Implement new create and manager trigger UI

Implements the new trigger setup user interface, which is now a linear workflow found on its own page, rather than a tiny modal dialog

Fixes #1187
This commit is contained in:
Joseph Schorr 2016-09-27 16:52:34 +02:00
parent 21b09a7451
commit 8e863b8cf5
47 changed files with 1835 additions and 1068 deletions

View file

@ -0,0 +1,54 @@
/**
* An element which displays a list of selectable paths containing Dockerfiles.
*/
angular.module('quay').directive('dockerfilePathSelect', function () {
var directiveDefinitionObject = {
priority: 0,
templateUrl: '/static/directives/dockerfile-path-select.html',
replace: false,
transclude: true,
restrict: 'C',
scope: {
'currentPath': '=currentPath',
'isValidPath': '=?isValidPath',
'paths': '=paths',
'supportsFullListing': '=supportsFullListing'
},
controller: function($scope, $element) {
$scope.isUnknownPath = true;
$scope.selectedPath = null;
var checkPath = function() {
$scope.isUnknownPath = false;
$scope.isValidPath = false;
var path = $scope.currentPath || '';
if (path.length == 0 || path[0] != '/') {
return;
}
$scope.isValidPath = true;
if (!$scope.paths) {
return;
}
$scope.isUnknownPath = $scope.supportsFullListing && $scope.paths.indexOf(path) < 0;
};
$scope.setPath = function(path) {
$scope.currentPath = path;
$scope.selectedPath = null;
};
$scope.setSelectedPath = function(path) {
$scope.currentPath = path;
$scope.selectedPath = path;
};
$scope.$watch('currentPath', checkPath);
$scope.$watch('paths', checkPath);
}
};
return directiveDefinitionObject;
});

View file

@ -28,36 +28,29 @@ angular.module('quay').directive('dropdownSelect', function ($compile) {
}
$scope.placeholder = $scope.placeholder || '';
$scope.internalItem = null;
$scope.lookaheadSetup = false;
// Setup lookahead.
var input = $($element).find('.lookahead-input');
$scope.$watch('clearValue', function(cv) {
if (cv) {
if (cv && $scope.lookaheadSetup) {
$scope.selectedItem = null;
$(input).val('');
$(input).typeahead('val', '');
$(input).typeahead('close');
}
});
$scope.$watch('selectedItem', function(item) {
if ($scope.selectedItem == $scope.internalItem) {
// The item has already been set due to an internal action.
return;
}
if ($scope.selectedItem != null) {
$(input).val(item.toString());
} else {
$(input).val('');
if (item != null && $scope.lookaheadSetup) {
$(input).typeahead('val', item.toString());
$(input).typeahead('close');
}
});
$scope.$watch('lookaheadItems', function(items) {
$(input).off();
if (!items) {
return;
}
items = items || [];
var formattedItems = [];
for (var i = 0; i < items.length; ++i) {
@ -80,7 +73,10 @@ angular.module('quay').directive('dropdownSelect', function ($compile) {
});
dropdownHound.initialize();
$(input).typeahead({}, {
$(input).typeahead({
'hint': false,
'highlight': false
}, {
source: dropdownHound.ttAdapter(),
templates: {
'suggestion': function (datum) {
@ -92,7 +88,6 @@ angular.module('quay').directive('dropdownSelect', function ($compile) {
$(input).on('input', function(e) {
$scope.$apply(function() {
$scope.internalItem = null;
$scope.selectedItem = null;
if ($scope.handleInput) {
$scope.handleInput({'input': $(input).val()});
@ -102,7 +97,6 @@ angular.module('quay').directive('dropdownSelect', function ($compile) {
$(input).on('typeahead:selected', function(e, datum) {
$scope.$apply(function() {
$scope.internalItem = datum['item'] || datum['value'];
$scope.selectedItem = datum['item'] || datum['value'];
if ($scope.handleItemSelected) {
$scope.handleItemSelected({'datum': datum});
@ -111,6 +105,7 @@ angular.module('quay').directive('dropdownSelect', function ($compile) {
});
$rootScope.__dropdownSelectCounter++;
$scope.lookaheadSetup = true;
});
},
link: function(scope, element, attrs) {

View file

@ -0,0 +1,141 @@
/**
* An element which displays a linear workflow of sections, each completed in order before the next
* step is made visible.
*/
angular.module('quay').directive('linearWorkflow', function () {
var directiveDefinitionObject = {
priority: 0,
templateUrl: '/static/directives/linear-workflow.html',
replace: false,
transclude: true,
restrict: 'C',
scope: {
'workflowState': '=?workflowState',
'workflowComplete': '&workflowComplete',
'doneTitle': '@doneTitle'
},
controller: function($scope, $element, $timeout) {
$scope.sections = [];
$scope.nextSection = function() {
if (!$scope.currentSection.valid) { return; }
var currentIndex = $scope.currentSection.index;
if (currentIndex + 1 >= $scope.sections.length) {
$scope.workflowComplete();
return;
}
$scope.workflowState = $scope.sections[currentIndex + 1].id;
};
this.registerSection = function(sectionScope, sectionElement) {
// Add the section to the list.
var sectionInfo = {
'index': $scope.sections.length,
'id': sectionScope.sectionId,
'title': sectionScope.sectionTitle,
'scope': sectionScope,
'element': sectionElement
};
$scope.sections.push(sectionInfo);
// Add a watch on the `sectionValid` value on the section itself. If/when this value
// changes, we copy it over to the sectionInfo, so that the overall workflow can watch
// the change.
sectionScope.$watch('sectionValid', function(isValid) {
sectionInfo['valid'] = isValid;
if (!isValid) {
// Reset the sections back to this section.
updateState();
}
});
// Bind the `submitSection` callback to move to the next section when the user hits
// enter (which calls this method on the scope via an ng-submit set on a wrapping
// <form> tag).
sectionScope.submitSection = function() {
$scope.nextSection();
};
// Update the state of the workflow to account for the new section.
updateState();
};
var updateState = function() {
// Find the furthest state we can show.
var foundIndex = 0;
var maxValidIndex = -1;
$scope.sections.forEach(function(section, index) {
if (section.id == $scope.workflowState) {
foundIndex = index;
}
if (maxValidIndex == index - 1 && section.valid) {
maxValidIndex = index;
}
});
var minSectionIndex = Math.min(maxValidIndex + 1, foundIndex);
$scope.sections.forEach(function(section, index) {
section.scope.sectionVisible = index <= minSectionIndex;
section.scope.isCurrentSection = false;
});
$scope.workflowState = null;
if (minSectionIndex >= 0 && minSectionIndex < $scope.sections.length) {
$scope.currentSection = $scope.sections[minSectionIndex];
$scope.workflowState = $scope.currentSection.id;
$scope.currentSection.scope.isCurrentSection = true;
// Focus to the first input (if any) in the section.
$timeout(function() {
var inputs = $scope.currentSection.element.find('input');
if (inputs.length == 1) {
inputs.focus();
}
}, 10);
}
};
$scope.$watch('workflowState', updateState);
}
};
return directiveDefinitionObject;
});
/**
* An element which displays a single section in a linear workflow.
*/
angular.module('quay').directive('linearWorkflowSection', function () {
var directiveDefinitionObject = {
priority: 0,
templateUrl: '/static/directives/linear-workflow-section.html',
replace: false,
transclude: true,
restrict: 'C',
require: '^linearWorkflow',
scope: {
'sectionId': '@sectionId',
'sectionTitle': '@sectionTitle',
'sectionValid': '=?sectionValid',
},
link: function($scope, $element, $attrs, $ctrl) {
$ctrl.registerSection($scope, $element);
},
controller: function($scope, $element) {
$scope.$watch('sectionVisible', function(visible) {
if (visible) {
$element.show();
} else {
$element.hide();
}
});
}
};
return directiveDefinitionObject;
});

View file

@ -0,0 +1,27 @@
/**
* An element which displays the setup and management workflow for a custom git trigger.
*/
angular.module('quay').directive('manageTriggerCustomGit', function () {
var directiveDefinitionObject = {
priority: 0,
templateUrl: '/static/directives/manage-trigger-custom-git.html',
replace: false,
transclude: true,
restrict: 'C',
scope: {
'trigger': '=trigger',
'activateTrigger': '&activateTrigger'
},
controller: function($scope, $element) {
$scope.config = {};
$scope.currentState = null;
$scope.$watch('trigger', function(trigger) {
if (trigger) {
$scope.config = trigger['config'] || {};
}
});
}
};
return directiveDefinitionObject;
});

View file

@ -0,0 +1,306 @@
/**
* An element which displays the setup and management workflow for a normal SCM git trigger.
*/
angular.module('quay').directive('manageTriggerGithost', function () {
var directiveDefinitionObject = {
priority: 0,
templateUrl: '/static/directives/manage-trigger-githost.html',
replace: false,
transclude: true,
restrict: 'C',
scope: {
'repository': '=repository',
'trigger': '=trigger',
'activateTrigger': '&activateTrigger'
},
controller: function($scope, $element, ApiService, TableService, TriggerService, RolesService) {
$scope.TableService = TableService;
$scope.config = {};
$scope.local = {};
$scope.currentState = null;
$scope.namespacesPerPage = 10;
$scope.repositoriesPerPage = 10;
$scope.robotsPerPage = 10;
$scope.local.namespaceOptions = {
'filter': '',
'predicate': 'score',
'reverse': false,
'page': 0
};
$scope.local.repositoryOptions = {
'filter': '',
'predicate': 'last_updated',
'reverse': false,
'page': 0,
'hideStale': true
};
$scope.local.robotOptions = {
'filter': '',
'predicate': 'can_read',
'reverse': false,
'page': 0
};
$scope.getTriggerIcon = function() {
return TriggerService.getIcon($scope.trigger.service);
};
$scope.createTrigger = function() {
var config = {
'build_source': $scope.local.selectedRepository.full_name,
'subdir': $scope.local.dockerfilePath.substr(1) // Remove starting /
};
if ($scope.local.triggerOptions.hasBranchTagFilter &&
$scope.local.triggerOptions.branchTagFilter) {
config['branchtag_regex'] = $scope.local.triggerOptions.branchTagFilter;
}
var activate = function() {
$scope.activateTrigger({'config': config, 'pull_robot': $scope.local.robotAccount});
};
if ($scope.local.robotAccount) {
if ($scope.local.robotAccount.can_read) {
activate();
} else {
// Add read permission onto the base repository for the robot and then activate the
// trigger.
var robot_name = $scope.local.robotAccount.name;
RolesService.setRepositoryRole($scope.repository, 'read', 'robot', robot_name, activate);
}
} else {
activate();
}
};
var buildOrderedNamespaces = function() {
if (!$scope.local.namespaces) {
return;
}
var namespaces = $scope.local.namespaces || [];
$scope.local.orderedNamespaces = TableService.buildOrderedItems(namespaces,
$scope.local.namespaceOptions,
['id'],
['score'])
$scope.local.maxScore = 0;
namespaces.forEach(function(namespace) {
$scope.local.maxScore = Math.max(namespace.score, $scope.local.maxScore);
});
};
var loadNamespaces = function() {
$scope.local.namespaces = null;
$scope.local.selectedNamespace = null;
$scope.local.orderedNamespaces = null;
$scope.local.selectedRepository = null;
$scope.local.orderedRepositories = null;
var params = {
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
'trigger_uuid': $scope.trigger.id
};
ApiService.listTriggerBuildSourceNamespaces(null, params).then(function(resp) {
$scope.local.namespaces = resp['namespaces'];
$scope.local.repositories = null;
buildOrderedNamespaces();
}, ApiService.errorDisplay('Could not retrieve the list of ' + $scope.namespaceTitle))
};
var buildOrderedRepositories = function() {
if (!$scope.local.repositories) {
return;
}
var repositories = $scope.local.repositories || [];
repositories.forEach(function(repository) {
repository['last_updated_datetime'] = new Date(repository['last_updated'] * 1000);
});
if ($scope.local.repositoryOptions.hideStale) {
var existingRepositories = repositories;
repositories = repositories.filter(function(repository) {
var older_date = moment(repository['last_updated_datetime']).add(1, 'months');
return !moment().isAfter(older_date);
});
if (existingRepositories.length > 0 && repositories.length == 0) {
repositories = existingRepositories;
}
}
$scope.local.orderedRepositories = TableService.buildOrderedItems(repositories,
$scope.local.repositoryOptions,
['name', 'description'],
[]);
};
var loadRepositories = function(namespace) {
$scope.local.repositories = null;
$scope.local.selectedRepository = null;
$scope.local.repositoryRefs = null;
$scope.local.triggerOptions = {
'hasBranchTagFilter': false
};
$scope.local.orderedRepositories = null;
var params = {
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
'trigger_uuid': $scope.trigger.id
};
var data = {
'namespace': namespace.id
};
ApiService.listTriggerBuildSources(data, params).then(function(resp) {
if (namespace == $scope.local.selectedNamespace) {
$scope.local.repositories = resp['sources'];
buildOrderedRepositories();
}
}, ApiService.errorDisplay('Could not retrieve repositories'));
};
var loadRepositoryRefs = function(repository) {
$scope.local.repositoryRefs = null;
$scope.local.triggerOptions = {
'hasBranchTagFilter': false
};
var params = {
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
'trigger_uuid': $scope.trigger.id,
'field_name': 'refs'
};
var config = {
'build_source': repository.full_name
};
ApiService.listTriggerFieldValues(config, params).then(function(resp) {
if (repository == $scope.local.selectedRepository) {
$scope.local.repositoryRefs = resp['values'];
$scope.local.repositoryFullRefs = resp['values'].map(function(ref) {
var kind = ref.kind == 'branch' ? 'heads' : 'tags';
var icon = ref.kind == 'branch' ? 'fa-code-fork' : 'fa-tag';
return {
'value': kind + '/' + ref.name,
'icon': icon,
'title': ref.name
};
});
}
}, ApiService.errorDisplay('Could not retrieve repository refs'));
};
var loadDockerfileLocations = function(repository) {
$scope.local.dockerfilePath = null;
var params = {
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
'trigger_uuid': $scope.trigger.id
};
var config = {
'build_source': repository.full_name
};
ApiService.listBuildTriggerSubdirs(config, params).then(function(resp) {
if (repository == $scope.local.selectedRepository) {
$scope.local.dockerfileLocations = resp;
}
}, ApiService.errorDisplay('Could not retrieve Dockerfile locations'));
};
var buildOrderedRobotAccounts = function() {
if (!$scope.local.triggerAnalysis || !$scope.local.triggerAnalysis.robots) {
return;
}
var robots = $scope.local.triggerAnalysis.robots;
$scope.local.orderedRobotAccounts = TableService.buildOrderedItems(robots,
$scope.local.robotOptions,
['name'],
[]);
};
var checkDockerfilePath = function(repository, path) {
$scope.local.triggerAnalysis = null;
$scope.local.robotAccount = null;
var params = {
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
'trigger_uuid': $scope.trigger.id
};
var config = {
'build_source': repository.full_name,
'subdir': path.substr(1)
};
var data = {
'config': config
};
ApiService.analyzeBuildTrigger(data, params).then(function(resp) {
$scope.local.triggerAnalysis = resp;
buildOrderedRobotAccounts();
}, ApiService.errorDisplay('Could not analyze trigger'));
};
$scope.$watch('trigger', function(trigger) {
if (trigger && $scope.repository) {
$scope.config = trigger['config'] || {};
$scope.namespaceTitle = 'organization';
$scope.local.selectedNamespace = null;
loadNamespaces();
}
});
$scope.$watch('local.selectedNamespace', function(namespace) {
if (namespace) {
loadRepositories(namespace);
}
});
$scope.$watch('local.selectedRepository', function(repository) {
if (repository) {
loadRepositoryRefs(repository);
loadDockerfileLocations(repository);
}
});
$scope.$watch('local.dockerfilePath', function(path) {
if (path && $scope.local.selectedRepository) {
checkDockerfilePath($scope.local.selectedRepository, path);
}
});
$scope.$watch('local.namespaceOptions.predicate', buildOrderedNamespaces);
$scope.$watch('local.namespaceOptions.reverse', buildOrderedNamespaces);
$scope.$watch('local.namespaceOptions.filter', buildOrderedNamespaces);
$scope.$watch('local.repositoryOptions.predicate', buildOrderedRepositories);
$scope.$watch('local.repositoryOptions.reverse', buildOrderedRepositories);
$scope.$watch('local.repositoryOptions.filter', buildOrderedRepositories);
$scope.$watch('local.repositoryOptions.hideStale', buildOrderedRepositories);
$scope.$watch('local.robotOptions.predicate', buildOrderedRobotAccounts);
$scope.$watch('local.robotOptions.reverse', buildOrderedRobotAccounts);
$scope.$watch('local.robotOptions.filter', buildOrderedRobotAccounts);
}
};
return directiveDefinitionObject;
});

View file

@ -0,0 +1,36 @@
/**
* An element which displays the matches and non-matches for a regular expression against a set of
* items.
*/
angular.module('quay').directive('regexMatchView', function () {
var directiveDefinitionObject = {
priority: 0,
templateUrl: '/static/directives/regex-match-view.html',
replace: false,
transclude: true,
restrict: 'C',
scope: {
'regex': '=regex',
'items': '=items'
},
controller: function($scope, $element) {
$scope.filterMatches = function(regexstr, items, shouldMatch) {
regexstr = regexstr || '.+';
try {
var regex = new RegExp(regexstr);
} catch (ex) {
return null;
}
return items.filter(function(item) {
var value = item['value'];
var m = value.match(regex);
var matches = !!(m && m[0].length == value.length);
return matches == shouldMatch;
});
};
}
};
return directiveDefinitionObject;
});

View file

@ -1,126 +0,0 @@
/**
* An element which displays the steps of the wizard-like dialog, changing them as each step
* is completed.
*/
angular.module('quay').directive('stepView', function ($compile) {
var directiveDefinitionObject = {
priority: 0,
templateUrl: '/static/directives/step-view.html',
replace: true,
transclude: true,
restrict: 'C',
scope: {
'nextStepCounter': '=nextStepCounter',
'currentStepValid': '=currentStepValid',
'stepsCompleted': '&stepsCompleted'
},
controller: function($scope, $element, $rootScope) {
var currentStepIndex = -1;
var steps = [];
var watcher = null;
// Members on 'this' are accessed by the individual steps.
this.register = function(scope, element) {
element.hide();
steps.push({
'scope': scope,
'element': element
});
nextStep();
};
var getCurrentStep = function() {
return steps[currentStepIndex];
};
var reset = function() {
currentStepIndex = -1;
for (var i = 0; i < steps.length; ++i) {
steps[i].element.hide();
}
$scope.currentStepValid = false;
};
var next = function() {
if (currentStepIndex >= 0) {
var currentStep = getCurrentStep();
if (!currentStep || !currentStep.scope) { return; }
if (!currentStep.scope.completeCondition) {
return;
}
currentStep.element.hide();
if (unwatch) {
unwatch();
unwatch = null;
}
}
currentStepIndex++;
if (currentStepIndex < steps.length) {
var currentStep = getCurrentStep();
currentStep.element.show();
currentStep.scope.load()
unwatch = currentStep.scope.$watch('completeCondition', function(cc) {
$scope.currentStepValid = !!cc;
});
} else {
$scope.stepsCompleted();
}
};
var nextStep = function() {
if (!steps || !steps.length) { return; }
if ($scope.nextStepCounter >= 0) {
next();
} else {
reset();
}
};
$scope.$watch('nextStepCounter', nextStep);
}
};
return directiveDefinitionObject;
});
/**
* A step in the step view.
*/
angular.module('quay').directive('stepViewStep', function () {
var directiveDefinitionObject = {
priority: 1,
require: '^stepView',
templateUrl: '/static/directives/step-view-step.html',
replace: false,
transclude: true,
restrict: 'C',
scope: {
'completeCondition': '=completeCondition',
'loadCallback': '&loadCallback',
'loadMessage': '@loadMessage'
},
link: function(scope, element, attrs, controller) {
controller.register(scope, element);
},
controller: function($scope, $element) {
$scope.load = function() {
$scope.loading = true;
$scope.loadCallback({'callback': function() {
$scope.loading = false;
}});
};
}
};
return directiveDefinitionObject;
});

View file

@ -1,49 +0,0 @@
/**
* An element which displays custom git-specific setup information for its build triggers.
*/
angular.module('quay').directive('triggerSetupCustom', function() {
var directiveDefinitionObject = {
priority: 0,
templateUrl: '/static/directives/trigger-setup-custom.html',
replace: false,
transclude: false,
restrict: 'C',
scope: {
'repository': '=repository',
'trigger': '=trigger',
'nextStepCounter': '=nextStepCounter',
'currentStepValid': '=currentStepValid',
'analyze': '&analyze'
},
controller: function($scope, $element, ApiService) {
$scope.analyzeCounter = 0;
$scope.setupReady = false;
$scope.state = {
'build_source': null,
'subdir': null
};
$scope.stepsCompleted = function() {
$scope.analyze({'isValid': $scope.state.build_source != null && $scope.state.subdir != null});
};
$scope.$watch('state.build_source', function(build_source) {
$scope.trigger['config']['build_source'] = build_source;
});
$scope.$watch('state.subdir', function(subdir) {
$scope.trigger['config']['subdir'] = subdir;
$scope.trigger.$ready = subdir != null;
});
$scope.nopLoad = function(callback) {
callback();
};
}
};
return directiveDefinitionObject;
});

View file

@ -1,242 +0,0 @@
/**
* An element which displays hosted Git (GitHub, Bitbucket)-specific setup information for its build triggers.
*/
angular.module('quay').directive('triggerSetupGithost', function () {
var directiveDefinitionObject = {
priority: 0,
templateUrl: '/static/directives/trigger-setup-githost.html',
replace: false,
transclude: false,
restrict: 'C',
scope: {
'repository': '=repository',
'trigger': '=trigger',
'kind': '@kind',
'nextStepCounter': '=nextStepCounter',
'currentStepValid': '=currentStepValid',
'analyze': '&analyze'
},
controller: function($scope, $element, ApiService, TriggerService) {
$scope.analyzeCounter = 0;
$scope.setupReady = false;
$scope.refs = null;
$scope.branchNames = null;
$scope.tagNames = null;
$scope.state = {
'currentRepo': null,
'branchTagFilter': '',
'hasBranchTagFilter': false,
'isInvalidLocation': true,
'currentLocation': null
};
var checkLocation = function() {
var location = $scope.state.currentLocation || '';
$scope.state.isInvalidLocation = $scope.supportsFullListing &&
$scope.locations.indexOf(location) < 0;
};
$scope.isMatching = function(kind, name, filter) {
try {
var patt = new RegExp(filter);
} catch (ex) {
return false;
}
var fullname = (kind + '/' + name);
var m = fullname.match(patt);
return m && m[0].length == fullname.length;
}
$scope.addRef = function(kind, name) {
if ($scope.isMatching(kind, name, $scope.state.branchTagFilter)) {
return;
}
var newFilter = kind + '/' + name;
var existing = $scope.state.branchTagFilter;
if (existing) {
$scope.state.branchTagFilter = '(' + existing + ')|(' + newFilter + ')';
} else {
$scope.state.branchTagFilter = newFilter;
}
}
$scope.stepsCompleted = function() {
$scope.analyze({'isValid': !$scope.state.isInvalidLocation});
};
$scope.loadRepositories = function(callback) {
if (!$scope.trigger || !$scope.repository) { return; }
var params = {
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
'trigger_uuid': $scope.trigger.id
};
ApiService.listTriggerBuildSources(null, params).then(function(resp) {
$scope.orgs = resp['sources'];
setupTypeahead();
callback();
}, ApiService.errorDisplay('Cannot load repositories'));
};
$scope.loadBranchesAndTags = function(callback) {
if (!$scope.trigger || !$scope.repository) { return; }
var params = {
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
'trigger_uuid': $scope.trigger['id'],
'field_name': 'refs'
};
ApiService.listTriggerFieldValues($scope.trigger['config'], params).then(function(resp) {
$scope.refs = resp['values'];
$scope.branchNames = [];
$scope.tagNames = [];
for (var i = 0; i < $scope.refs.length; ++i) {
var ref = $scope.refs[i];
if (ref.kind == 'branch') {
$scope.branchNames.push(ref.name);
} else {
$scope.tagNames.push(ref.name);
}
}
callback();
}, ApiService.errorDisplay('Cannot load branch and tag names'));
};
$scope.loadLocations = function(callback) {
if (!$scope.trigger || !$scope.repository) { return; }
$scope.locations = null;
var params = {
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
'trigger_uuid': $scope.trigger.id
};
ApiService.listBuildTriggerSubdirs($scope.trigger['config'], params).then(function(resp) {
if (resp['status'] == 'error') {
$scope.locations = [];
callback(resp['message'] || 'Could not load Dockerfile locations');
return;
}
$scope.locations = resp['subdir'] || [];
// Select a default location (if any).
if ($scope.locations.length > 0) {
$scope.setLocation($scope.locations[0]);
} else {
$scope.state.currentLocation = null;
$scope.trigger.$ready = true;
checkLocation();
}
callback();
}, ApiService.errorDisplay('Cannot load locations'));
}
$scope.handleLocationInput = function(location) {
$scope.trigger['config']['subdir'] = location || '';
$scope.trigger.$ready = true;
checkLocation();
};
$scope.handleLocationSelected = function(datum) {
$scope.setLocation(datum['value']);
};
$scope.setLocation = function(location) {
$scope.state.currentLocation = location;
$scope.trigger['config']['subdir'] = location || '';
$scope.trigger.$ready = true;
checkLocation();
};
$scope.selectRepo = function(repo, org) {
$scope.state.currentRepo = {
'repo': repo,
'avatar_url': org['info']['avatar_url'],
'toString': function() {
return this.repo;
}
};
};
$scope.selectRepoInternal = function(currentRepo) {
$scope.trigger.$ready = false;
var params = {
'repository': $scope.repository.namespace + '/' + $scope.repository.name,
'trigger_uuid': $scope.trigger['id']
};
var repo = currentRepo['repo'];
$scope.trigger['config'] = {
'build_source': repo,
'subdir': ''
};
};
$scope.scmIcon = function(kind) {
return TriggerService.getIcon(kind);
};
var setupTypeahead = function() {
var repos = [];
for (var i = 0; i < $scope.orgs.length; ++i) {
var org = $scope.orgs[i];
var orepos = org['repos'];
for (var j = 0; j < orepos.length; ++j) {
var repoValue = {
'repo': orepos[j],
'avatar_url': org['info']['avatar_url'],
'toString': function() {
return this.repo;
}
};
var datum = {
'name': orepos[j],
'org': org,
'value': orepos[j],
'title': orepos[j],
'item': repoValue
};
repos.push(datum);
}
}
$scope.repoLookahead = repos;
};
$scope.$watch('trigger', function(trigger) {
if (!trigger) { return; }
$scope.supportsFullListing = TriggerService.supportsFullListing(trigger.service)
});
$scope.$watch('state.currentRepo', function(repo) {
if (repo) {
$scope.selectRepoInternal(repo);
}
});
$scope.$watch('state.branchTagFilter', function(bf) {
if (!$scope.trigger) { return; }
if ($scope.state.hasBranchTagFilter) {
$scope.trigger['config']['branchtag_regex'] = bf;
} else {
delete $scope.trigger['config']['branchtag_regex'];
}
});
}
};
return directiveDefinitionObject;
});