summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/dispatcher.js4
-rw-r--r--app/assets/javascripts/gl_dropdown.js6
-rw-r--r--app/assets/javascripts/protected_branch_access_dropdown.js.es624
-rw-r--r--app/assets/javascripts/protected_branch_create.js.es656
-rw-r--r--app/assets/javascripts/protected_branch_dropdown.js.es675
-rw-r--r--app/assets/javascripts/protected_branch_edit.js.es661
-rw-r--r--app/assets/javascripts/protected_branch_edit_list.js.es617
-rw-r--r--app/assets/javascripts/protected_branch_select.js72
-rw-r--r--app/assets/javascripts/protected_branches_access_select.js.es663
-rw-r--r--app/assets/javascripts/users_select.js38
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss8
-rw-r--r--app/assets/stylesheets/framework/panels.scss5
-rw-r--r--app/assets/stylesheets/pages/projects.scss23
-rw-r--r--app/models/concerns/faster_cache_keys.rb16
-rw-r--r--app/models/issue.rb1
-rw-r--r--app/models/note.rb1
-rw-r--r--app/models/project.rb11
-rw-r--r--app/views/projects/protected_branches/_branches_list.html.haml50
-rw-r--r--app/views/projects/protected_branches/_create_protected_branch.html.haml36
-rw-r--r--app/views/projects/protected_branches/_dropdown.html.haml12
-rw-r--r--app/views/projects/protected_branches/_protected_branch.html.haml17
-rw-r--r--app/views/projects/protected_branches/index.html.haml36
-rw-r--r--app/views/shared/web_hooks/_form.html.haml2
-rw-r--r--app/workers/post_receive.rb4
24 files changed, 399 insertions, 239 deletions
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js
index 9e6901962c6..20f2b1d69b5 100644
--- a/app/assets/javascripts/dispatcher.js
+++ b/app/assets/javascripts/dispatcher.js
@@ -173,8 +173,8 @@
new Search();
break;
case 'projects:protected_branches:index':
- new ProtectedBranchesAccessSelect($(".new_protected_branch"), false, true);
- new ProtectedBranchesAccessSelect($(".protected-branches-list"), true, false);
+ new gl.ProtectedBranchCreate();
+ new gl.ProtectedBranchEditList();
break;
}
switch (path.first()) {
diff --git a/app/assets/javascripts/gl_dropdown.js b/app/assets/javascripts/gl_dropdown.js
index cc7e422fd89..d3394fae3f9 100644
--- a/app/assets/javascripts/gl_dropdown.js
+++ b/app/assets/javascripts/gl_dropdown.js
@@ -607,7 +607,7 @@
return this.dropdown.before($input);
};
- GitLabDropdown.prototype.selectRowAtIndex = function(e, index) {
+ GitLabDropdown.prototype.selectRowAtIndex = function(index) {
var $el, selector;
selector = ".dropdown-content li:not(.divider,.dropdown-header,.separator):eq(" + index + ") a";
if (this.dropdown.find(".dropdown-toggle-page").length) {
@@ -615,8 +615,6 @@
}
$el = $(selector, this.dropdown);
if ($el.length) {
- e.preventDefault();
- e.stopImmediatePropagation();
return $el.first().trigger('click');
}
};
@@ -653,7 +651,7 @@
return false;
}
if (currentKeyCode === 13 && currentIndex !== -1) {
- return _this.selectRowAtIndex(e, $('.is-focused', _this.dropdown).closest('li').index() - 1);
+ return _this.selectRowAtIndex($('.is-focused', _this.dropdown).closest('li').index() - 1);
}
};
})(this));
diff --git a/app/assets/javascripts/protected_branch_access_dropdown.js.es6 b/app/assets/javascripts/protected_branch_access_dropdown.js.es6
new file mode 100644
index 00000000000..2fbb088fa04
--- /dev/null
+++ b/app/assets/javascripts/protected_branch_access_dropdown.js.es6
@@ -0,0 +1,24 @@
+(global => {
+ global.gl = global.gl || {};
+
+ gl.ProtectedBranchAccessDropdown = class {
+ constructor(options) {
+ const { $dropdown, data, onSelect } = options;
+
+ $dropdown.glDropdown({
+ data: data,
+ selectable: true,
+ inputId: $dropdown.data('input-id'),
+ fieldName: $dropdown.data('field-name'),
+ toggleLabel(item) {
+ return item.text;
+ },
+ clicked(item, $el, e) {
+ e.preventDefault();
+ onSelect();
+ }
+ });
+ }
+ }
+
+})(window);
diff --git a/app/assets/javascripts/protected_branch_create.js.es6 b/app/assets/javascripts/protected_branch_create.js.es6
new file mode 100644
index 00000000000..00e20a03b04
--- /dev/null
+++ b/app/assets/javascripts/protected_branch_create.js.es6
@@ -0,0 +1,56 @@
+(global => {
+ global.gl = global.gl || {};
+
+ gl.ProtectedBranchCreate = class {
+ constructor() {
+ this.$wrap = this.$form = $('#new_protected_branch');
+ this.buildDropdowns();
+ }
+
+ buildDropdowns() {
+ const $allowedToMergeDropdown = this.$wrap.find('.js-allowed-to-merge');
+ const $allowedToPushDropdown = this.$wrap.find('.js-allowed-to-push');
+
+ // Cache callback
+ this.onSelectCallback = this.onSelect.bind(this);
+
+ // Allowed to Merge dropdown
+ new gl.ProtectedBranchAccessDropdown({
+ $dropdown: $allowedToMergeDropdown,
+ data: gon.merge_access_levels,
+ onSelect: this.onSelectCallback
+ });
+
+ // Allowed to Push dropdown
+ new gl.ProtectedBranchAccessDropdown({
+ $dropdown: $allowedToPushDropdown,
+ data: gon.push_access_levels,
+ onSelect: this.onSelectCallback
+ });
+
+ // Select default
+ $allowedToPushDropdown.data('glDropdown').selectRowAtIndex(0);
+ $allowedToMergeDropdown.data('glDropdown').selectRowAtIndex(0);
+
+ // Protected branch dropdown
+ new ProtectedBranchDropdown({
+ $dropdown: this.$wrap.find('.js-protected-branch-select'),
+ onSelect: this.onSelectCallback
+ });
+ }
+
+ // This will run after clicked callback
+ onSelect() {
+
+ // Enable submit button
+ const $branchInput = this.$wrap.find('input[name="protected_branch[name]"]');
+ const $allowedToMergeInput = this.$wrap.find('input[name="protected_branch[merge_access_level_attributes][access_level]"]');
+ const $allowedToPushInput = this.$wrap.find('input[name="protected_branch[push_access_level_attributes][access_level]"]');
+
+ if ($branchInput.val() && $allowedToMergeInput.val() && $allowedToPushInput.val()){
+ this.$form.find('input[type="submit"]').removeAttr('disabled');
+ }
+ }
+ }
+
+})(window);
diff --git a/app/assets/javascripts/protected_branch_dropdown.js.es6 b/app/assets/javascripts/protected_branch_dropdown.js.es6
new file mode 100644
index 00000000000..6738dc8862d
--- /dev/null
+++ b/app/assets/javascripts/protected_branch_dropdown.js.es6
@@ -0,0 +1,75 @@
+class ProtectedBranchDropdown {
+ constructor(options) {
+ this.onSelect = options.onSelect;
+ this.$dropdown = options.$dropdown;
+ this.$dropdownContainer = this.$dropdown.parent();
+ this.$dropdownFooter = this.$dropdownContainer.find('.dropdown-footer');
+ this.$protectedBranch = this.$dropdownContainer.find('.create-new-protected-branch');
+
+ this.buildDropdown();
+ this.bindEvents();
+
+ // Hide footer
+ this.$dropdownFooter.addClass('hidden');
+ }
+
+ buildDropdown() {
+ this.$dropdown.glDropdown({
+ data: this.getProtectedBranches.bind(this),
+ filterable: true,
+ remote: false,
+ search: {
+ fields: ['title']
+ },
+ selectable: true,
+ toggleLabel(selected) {
+ return (selected && 'id' in selected) ? selected.title : 'Protected Branch';
+ },
+ fieldName: 'protected_branch[name]',
+ text(protectedBranch) {
+ return _.escape(protectedBranch.title);
+ },
+ id(protectedBranch) {
+ return _.escape(protectedBranch.id);
+ },
+ onFilter: this.toggleCreateNewButton.bind(this),
+ clicked: (item, $el, e) => {
+ e.preventDefault();
+ this.onSelect();
+ }
+ });
+ }
+
+ bindEvents() {
+ this.$protectedBranch.on('click', this.onClickCreateWildcard.bind(this));
+ }
+
+ onClickCreateWildcard() {
+ this.$dropdown.data('glDropdown').remote.execute();
+ this.$dropdown.data('glDropdown').selectRowAtIndex(0);
+ }
+
+ getProtectedBranches(term, callback) {
+ if (this.selectedBranch) {
+ callback(gon.open_branches.concat(this.selectedBranch));
+ } else {
+ callback(gon.open_branches);
+ }
+ }
+
+ toggleCreateNewButton(branchName) {
+ this.selectedBranch = {
+ title: branchName,
+ id: branchName,
+ text: branchName
+ };
+
+ if (branchName) {
+ this.$dropdownContainer
+ .find('.create-new-protected-branch code')
+ .text(branchName);
+ }
+
+ this.$dropdownFooter.toggleClass('hidden', !branchName);
+ }
+}
diff --git a/app/assets/javascripts/protected_branch_edit.js.es6 b/app/assets/javascripts/protected_branch_edit.js.es6
new file mode 100644
index 00000000000..8d42e268ebc
--- /dev/null
+++ b/app/assets/javascripts/protected_branch_edit.js.es6
@@ -0,0 +1,61 @@
+(global => {
+ global.gl = global.gl || {};
+
+ gl.ProtectedBranchEdit = class {
+ constructor(options) {
+ this.$wrap = options.$wrap;
+ this.$allowedToMergeDropdown = this.$wrap.find('.js-allowed-to-merge');
+ this.$allowedToPushDropdown = this.$wrap.find('.js-allowed-to-push');
+
+ this.buildDropdowns();
+ }
+
+ buildDropdowns() {
+
+ // Allowed to merge dropdown
+ new gl.ProtectedBranchAccessDropdown({
+ $dropdown: this.$allowedToMergeDropdown,
+ data: gon.merge_access_levels,
+ onSelect: this.onSelect.bind(this)
+ });
+
+ // Allowed to push dropdown
+ new gl.ProtectedBranchAccessDropdown({
+ $dropdown: this.$allowedToPushDropdown,
+ data: gon.push_access_levels,
+ onSelect: this.onSelect.bind(this)
+ });
+ }
+
+ onSelect() {
+ const $allowedToMergeInput = this.$wrap.find(`input[name="${this.$allowedToMergeDropdown.data('fieldName')}"]`);
+ const $allowedToPushInput = this.$wrap.find(`input[name="${this.$allowedToPushDropdown.data('fieldName')}"]`);
+
+ $.ajax({
+ type: 'POST',
+ url: this.$wrap.data('url'),
+ dataType: 'json',
+ data: {
+ _method: 'PATCH',
+ id: this.$wrap.data('banchId'),
+ protected_branch: {
+ merge_access_level_attributes: {
+ access_level: $allowedToMergeInput.val()
+ },
+ push_access_level_attributes: {
+ access_level: $allowedToPushInput.val()
+ }
+ }
+ },
+ success: () => {
+ this.$wrap.effect('highlight');
+ },
+ error() {
+ $.scrollTo(0);
+ new Flash('Failed to update branch!');
+ }
+ });
+ }
+ }
+
+})(window);
diff --git a/app/assets/javascripts/protected_branch_edit_list.js.es6 b/app/assets/javascripts/protected_branch_edit_list.js.es6
new file mode 100644
index 00000000000..9ff0fd12c76
--- /dev/null
+++ b/app/assets/javascripts/protected_branch_edit_list.js.es6
@@ -0,0 +1,17 @@
+(global => {
+ global.gl = global.gl || {};
+
+ gl.ProtectedBranchEditList = class {
+ constructor() {
+ this.$wrap = $('.protected-branches-list');
+
+ // Build edit forms
+ this.$wrap.find('.js-protected-branch-edit-form').each((i, el) => {
+ new gl.ProtectedBranchEdit({
+ $wrap: $(el)
+ });
+ });
+ }
+ }
+
+})(window);
diff --git a/app/assets/javascripts/protected_branch_select.js b/app/assets/javascripts/protected_branch_select.js
deleted file mode 100644
index 3a47fc972dc..00000000000
--- a/app/assets/javascripts/protected_branch_select.js
+++ /dev/null
@@ -1,72 +0,0 @@
-(function() {
- var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
-
- this.ProtectedBranchSelect = (function() {
- function ProtectedBranchSelect(currentProject) {
- this.toggleCreateNewButton = bind(this.toggleCreateNewButton, this);
- this.getProtectedBranches = bind(this.getProtectedBranches, this);
- $('.dropdown-footer').hide();
- this.dropdown = $('.js-protected-branch-select').glDropdown({
- data: this.getProtectedBranches,
- filterable: true,
- remote: false,
- search: {
- fields: ['title']
- },
- selectable: true,
- toggleLabel: function(selected) {
- if (selected && 'id' in selected) {
- return selected.title;
- } else {
- return 'Protected Branch';
- }
- },
- fieldName: 'protected_branch[name]',
- text: function(protected_branch) {
- return _.escape(protected_branch.title);
- },
- id: function(protected_branch) {
- return _.escape(protected_branch.id);
- },
- onFilter: this.toggleCreateNewButton,
- clicked: function() {
- return $('.protect-branch-btn').attr('disabled', false);
- }
- });
- $('.create-new-protected-branch').on('click', (function(_this) {
- return function(event) {
- _this.dropdown.data('glDropdown').remote.execute();
- return _this.dropdown.data('glDropdown').selectRowAtIndex(event, 0);
- };
- })(this));
- }
-
- ProtectedBranchSelect.prototype.getProtectedBranches = function(term, callback) {
- if (this.selectedBranch) {
- return callback(gon.open_branches.concat(this.selectedBranch));
- } else {
- return callback(gon.open_branches);
- }
- };
-
- ProtectedBranchSelect.prototype.toggleCreateNewButton = function(branchName) {
- this.selectedBranch = {
- title: branchName,
- id: branchName,
- text: branchName
- };
- if (branchName === '') {
- $('.protected-branch-select-footer-list').addClass('hidden');
- return $('.dropdown-footer').hide();
- } else {
- $('.create-new-protected-branch').text("Create Protected Branch: " + branchName);
- $('.protected-branch-select-footer-list').removeClass('hidden');
- return $('.dropdown-footer').show();
- }
- };
-
- return ProtectedBranchSelect;
-
- })();
-
-}).call(this);
diff --git a/app/assets/javascripts/protected_branches_access_select.js.es6 b/app/assets/javascripts/protected_branches_access_select.js.es6
deleted file mode 100644
index e98312bbf37..00000000000
--- a/app/assets/javascripts/protected_branches_access_select.js.es6
+++ /dev/null
@@ -1,63 +0,0 @@
-class ProtectedBranchesAccessSelect {
- constructor(container, saveOnSelect, selectDefault) {
- this.container = container;
- this.saveOnSelect = saveOnSelect;
-
- this.container.find(".allowed-to-merge").each((i, element) => {
- var fieldName = $(element).data('field-name');
- var dropdown = $(element).glDropdown({
- data: gon.merge_access_levels,
- selectable: true,
- fieldName: fieldName,
- clicked: _.chain(this.onSelect).partial(element).bind(this).value()
- });
-
- if (selectDefault) {
- dropdown.data('glDropdown').selectRowAtIndex(document.createEvent("Event"), 0);
- }
- });
-
-
- this.container.find(".allowed-to-push").each((i, element) => {
- var fieldName = $(element).data('field-name');
- var dropdown = $(element).glDropdown({
- data: gon.push_access_levels,
- selectable: true,
- fieldName: fieldName,
- clicked: _.chain(this.onSelect).partial(element).bind(this).value()
- });
-
- if (selectDefault) {
- dropdown.data('glDropdown').selectRowAtIndex(document.createEvent("Event"), 0);
- }
- });
- }
-
- onSelect(dropdown, selected, element, e) {
- $(dropdown).find('.dropdown-toggle-text').text(selected.text);
- if (this.saveOnSelect) {
- return $.ajax({
- type: "POST",
- url: $(dropdown).data('url'),
- dataType: "json",
- data: {
- _method: 'PATCH',
- id: $(dropdown).data('id'),
- protected_branch: {
- ["" + ($(dropdown).data('type')) + "_attributes"]: {
- "access_level": selected.id
- }
- }
- },
- success: function() {
- var row;
- row = $(e.target);
- return row.closest('tr').effect('highlight');
- },
- error: function() {
- return new Flash("Failed to update branch!", "alert");
- }
- });
- }
- }
-}
diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js
index 4af2a214e12..65d362e072c 100644
--- a/app/assets/javascripts/users_select.js
+++ b/app/assets/javascripts/users_select.js
@@ -13,14 +13,15 @@
}
$('.js-user-search').each((function(_this) {
return function(i, dropdown) {
+ var options = {};
var $block, $collapsedSidebar, $dropdown, $loading, $selectbox, $value, abilityName, assignTo, assigneeTemplate, collapsedAssigneeTemplate, defaultLabel, firstUser, issueURL, selectedId, showAnyUser, showNullUser;
$dropdown = $(dropdown);
- _this.projectId = $dropdown.data('project-id');
- _this.showCurrentUser = $dropdown.data('current-user');
+ options.projectId = $dropdown.data('project-id');
+ options.showCurrentUser = $dropdown.data('current-user');
showNullUser = $dropdown.data('null-user');
showAnyUser = $dropdown.data('any-user');
firstUser = $dropdown.data('first-user');
- _this.authorId = $dropdown.data('author-id');
+ options.authorId = $dropdown.data('author-id');
selectedId = $dropdown.data('selected');
defaultLabel = $dropdown.data('default-label');
issueURL = $dropdown.data('issueUpdate');
@@ -75,7 +76,7 @@
data: function(term, callback) {
var isAuthorFilter;
isAuthorFilter = $('.js-author-search');
- return _this.users(term, function(users) {
+ return _this.users(term, options, function(users) {
var anyUser, index, j, len, name, obj, showDivider;
if (term.length === 0) {
showDivider = 0;
@@ -185,11 +186,14 @@
$('.ajax-users-select').each((function(_this) {
return function(i, select) {
var firstUser, showAnyUser, showEmailUser, showNullUser;
- _this.projectId = $(select).data('project-id');
- _this.groupId = $(select).data('group-id');
- _this.showCurrentUser = $(select).data('current-user');
- _this.authorId = $(select).data('author-id');
- _this.skipUsers = $(select).data('skip-users');
+ var options = {};
+ options.skipLdap = $(select).hasClass('skip_ldap');
+ options.projectId = $(select).data('project-id');
+ options.groupId = $(select).data('group-id');
+ options.showCurrentUser = $(select).data('current-user');
+ options.pushCodeToProtectedBranches = $(select).data('push-code-to-protected-branches');
+ options.authorId = $(select).data('author-id');
+ options.skipUsers = $(select).data('skip-users');
showNullUser = $(select).data('null-user');
showAnyUser = $(select).data('any-user');
showEmailUser = $(select).data('email-user');
@@ -199,7 +203,7 @@
multiple: $(select).hasClass('multiselect'),
minimumInputLength: 0,
query: function(query) {
- return _this.users(query.term, function(users) {
+ return _this.users(query.term, options, function(users) {
var anyUser, data, emailUser, index, j, len, name, nullUser, obj, ref;
data = {
results: users
@@ -309,7 +313,7 @@
});
};
- UsersSelect.prototype.users = function(query, callback) {
+ UsersSelect.prototype.users = function(query, options, callback) {
var url;
url = this.buildUrl(this.usersPath);
return $.ajax({
@@ -318,11 +322,13 @@
search: query,
per_page: 20,
active: true,
- project_id: this.projectId,
- group_id: this.groupId,
- current_user: this.showCurrentUser,
- author_id: this.authorId,
- skip_users: this.skipUsers
+ project_id: options.projectId || null,
+ group_id: options.groupId || null,
+ skip_ldap: options.skipLdap || null,
+ current_user: options.showCurrentUser || null,
+ push_code_to_protected_branches: options.pushCodeToProtectedBranches || null,
+ author_id: options.authorId || null,
+ skip_users: options.skipUsers || null
},
dataType: "json"
}).done(function(users) {
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index c54eb0d6479..e8eafa15899 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -72,6 +72,14 @@
&.large {
width: 200px;
}
+
+ &.wide {
+ width: 100%;
+
+ + .dropdown-select {
+ width: 100%;
+ }
+ }
}
.dropdown-menu,
diff --git a/app/assets/stylesheets/framework/panels.scss b/app/assets/stylesheets/framework/panels.scss
index 874416e1007..c6f30e144fd 100644
--- a/app/assets/stylesheets/framework/panels.scss
+++ b/app/assets/stylesheets/framework/panels.scss
@@ -23,4 +23,9 @@
margin-top: $gl-padding;
}
}
+
+ .panel-title {
+ font-size: inherit;
+ line-height: inherit;
+ }
}
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index d91c8e61165..cf9aa02600d 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -656,13 +656,9 @@ pre.light-well {
}
.new_protected_branch {
- .dropdown {
- display: inline;
- margin-left: 15px;
- }
-
label {
- min-width: 120px;
+ margin-top: 6px;
+ font-weight: normal;
}
}
@@ -678,6 +674,21 @@ pre.light-well {
font-weight: 600;
}
}
+
+ .settings-message {
+ margin: 0;
+ border-radius: 0 0 1px 1px;
+ padding: 20px 0;
+ border: none;
+ }
+
+ .table-bordered {
+ border-radius: 1px;
+
+ th:not(:last-child), td:not(:last-child) {
+ border-right: solid 1px transparent;
+ }
+ }
}
.custom-notifications-form {
diff --git a/app/models/concerns/faster_cache_keys.rb b/app/models/concerns/faster_cache_keys.rb
new file mode 100644
index 00000000000..5b14723fa2d
--- /dev/null
+++ b/app/models/concerns/faster_cache_keys.rb
@@ -0,0 +1,16 @@
+module FasterCacheKeys
+ # A faster version of Rails' "cache_key" method.
+ #
+ # Rails' default "cache_key" method uses all kind of complex logic to figure
+ # out the cache key. In many cases this complexity and overhead may not be
+ # needed.
+ #
+ # This method does not do any timestamp parsing as this process is quite
+ # expensive and not needed when generating cache keys. This method also relies
+ # on the table name instead of the cache namespace name as the latter uses
+ # complex logic to generate the exact same value (as when using the table
+ # name) in 99% of the cases.
+ def cache_key
+ "#{self.class.table_name}/#{id}-#{read_attribute_before_type_cast(:updated_at)}"
+ end
+end
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 11f734cfc6d..d62ffb21467 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -7,6 +7,7 @@ class Issue < ActiveRecord::Base
include Sortable
include Taskable
include Spammable
+ include FasterCacheKeys
DueDateStruct = Struct.new(:title, :name).freeze
NoDueDate = DueDateStruct.new('No Due Date', '0').freeze
diff --git a/app/models/note.rb b/app/models/note.rb
index b6b2ac6aa42..ddcd7f9d034 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -5,6 +5,7 @@ class Note < ActiveRecord::Base
include Mentionable
include Awardable
include Importable
+ include FasterCacheKeys
# Attribute containing rendered and redacted Markdown as generated by
# Banzai::ObjectRenderer.
diff --git a/app/models/project.rb b/app/models/project.rb
index a18aef09acd..a667857d058 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -865,10 +865,16 @@ class Project < ActiveRecord::Base
# Check if current branch name is marked as protected in the system
def protected_branch?(branch_name)
+ return true if empty_repo? && default_branch_protected?
+
@protected_branches ||= self.protected_branches.to_a
ProtectedBranch.matching(branch_name, protected_branches: @protected_branches).present?
end
+ def user_can_push_to_empty_repo?(user)
+ !default_branch_protected? || team.max_member_access(user.id) > Gitlab::Access::DEVELOPER
+ end
+
def forked?
!(forked_project_link.nil? || forked_project_link.forked_from_project.nil?)
end
@@ -1260,6 +1266,11 @@ class Project < ActiveRecord::Base
private
+ def default_branch_protected?
+ current_application_settings.default_branch_protection == Gitlab::Access::PROTECTION_FULL ||
+ current_application_settings.default_branch_protection == Gitlab::Access::PROTECTION_DEV_CAN_MERGE
+ end
+
def authorized_for_user_by_group?(user, min_access_level)
member = user.group_members.find_by(source_id: group)
diff --git a/app/views/projects/protected_branches/_branches_list.html.haml b/app/views/projects/protected_branches/_branches_list.html.haml
index 0603a014008..04b19a8c5a7 100644
--- a/app/views/projects/protected_branches/_branches_list.html.haml
+++ b/app/views/projects/protected_branches/_branches_list.html.haml
@@ -1,26 +1,28 @@
-%h5.prepend-top-0
- Already Protected (#{@protected_branches.size})
-- if @protected_branches.empty?
- %p.settings-message.text-center
- No branches are protected, protect a branch with the form above.
-- else
- - can_admin_project = can?(current_user, :admin_project, @project)
+.panel.panel-default.protected-branches-list
+ - if @protected_branches.empty?
+ .panel-heading
+ %h3.panel-title
+ Protected branch (#{@protected_branches.size})
+ %p.settings-message.text-center
+ There are currently no protected branches, protect a branch with the form above.
+ - else
+ - can_admin_project = can?(current_user, :admin_project, @project)
- %table.table.protected-branches-list
- %colgroup
- %col{ width: "20%" }
- %col{ width: "30%" }
- %col{ width: "25%" }
- %col{ width: "25%" }
- %thead
- %tr
- %th Branch
- %th Last commit
- %th Allowed to merge
- %th Allowed to push
- - if can_admin_project
- %th
- %tbody
- = render partial: @protected_branches, locals: { can_admin_project: can_admin_project }
+ %table.table.table-bordered
+ %colgroup
+ %col{ width: "25%" }
+ %col{ width: "30%" }
+ %col{ width: "25%" }
+ %col{ width: "20%" }
+ %thead
+ %tr
+ %th Protected branch (#{@protected_branches.size})
+ %th Last commit
+ %th Allowed to merge
+ %th Allowed to push
+ - if can_admin_project
+ %th
+ %tbody
+ = render partial: @protected_branches, locals: { can_admin_project: can_admin_project }
- = paginate @protected_branches, theme: 'gitlab'
+ = paginate @protected_branches, theme: 'gitlab'
diff --git a/app/views/projects/protected_branches/_create_protected_branch.html.haml b/app/views/projects/protected_branches/_create_protected_branch.html.haml
new file mode 100644
index 00000000000..85d0c494ba8
--- /dev/null
+++ b/app/views/projects/protected_branches/_create_protected_branch.html.haml
@@ -0,0 +1,36 @@
+= form_for [@project.namespace.becomes(Namespace), @project, @protected_branch] do |f|
+ .panel.panel-default
+ .panel-heading
+ %h3.panel-title
+ Protect a branch
+ .panel-body
+ .form-horizontal
+ .form-group
+ = f.label :name, class: 'col-md-2 text-right' do
+ Branch:
+ .col-md-10
+ = render partial: "dropdown", locals: { f: f }
+ .help-block
+ = link_to 'Wildcards', help_page_path('user/project/protected_branches', anchor: 'wildcard-protected-branches')
+ such as
+ %code *-stable
+ or
+ %code production/*
+ are supported
+ .form-group
+ %label.col-md-2.text-right{ for: 'merge_access_level_attributes' }
+ Allowed to merge:
+ .col-md-10
+ = dropdown_tag('Select',
+ options: { toggle_class: 'js-allowed-to-merge wide',
+ data: { field_name: 'protected_branch[merge_access_level_attributes][access_level]', input_id: 'merge_access_level_attributes' }})
+ .form-group
+ %label.col-md-2.text-right{ for: 'push_access_level_attributes' }
+ Allowed to push:
+ .col-md-10
+ = dropdown_tag('Select',
+ options: { toggle_class: 'js-allowed-to-push wide',
+ data: { field_name: 'protected_branch[push_access_level_attributes][access_level]', input_id: 'push_access_level_attributes' }})
+
+ .panel-footer
+ = f.submit 'Protect', class: 'btn-create btn', disabled: true
diff --git a/app/views/projects/protected_branches/_dropdown.html.haml b/app/views/projects/protected_branches/_dropdown.html.haml
index b803d932e67..a9e27df5a87 100644
--- a/app/views/projects/protected_branches/_dropdown.html.haml
+++ b/app/views/projects/protected_branches/_dropdown.html.haml
@@ -1,17 +1,15 @@
= f.hidden_field(:name)
-= dropdown_tag("Protected Branch",
- options: { title: "Pick protected branch", toggle_class: 'js-protected-branch-select js-filter-submit',
+= dropdown_tag('Select branch or create wildcard',
+ options: { toggle_class: 'js-protected-branch-select js-filter-submit wide',
filter: true, dropdown_class: "dropdown-menu-selectable", placeholder: "Search protected branches",
footer_content: true,
data: { show_no: true, show_any: true, show_upcoming: true,
selected: params[:protected_branch_name],
project_id: @project.try(:id) } }) do
- %ul.dropdown-footer-list.hidden.protected-branch-select-footer-list
+ %ul.dropdown-footer-list
%li
= link_to '#', title: "New Protected Branch", class: "create-new-protected-branch" do
- Create new
-
-:javascript
- new ProtectedBranchSelect();
+ Create wildcard
+ %code
diff --git a/app/views/projects/protected_branches/_protected_branch.html.haml b/app/views/projects/protected_branches/_protected_branch.html.haml
index 498e412235e..e2e01ee78f8 100644
--- a/app/views/projects/protected_branches/_protected_branch.html.haml
+++ b/app/views/projects/protected_branches/_protected_branch.html.haml
@@ -1,5 +1,4 @@
-- url = namespace_project_protected_branch_path(@project.namespace, @project, protected_branch)
-%tr
+%tr.js-protected-branch-edit-form{ data: { url: namespace_project_protected_branch_path(@project.namespace, @project, protected_branch), branch_id: protected_branch.id } }
%td
= protected_branch.name
- if @project.root_ref?(protected_branch.name)
@@ -16,14 +15,14 @@
(branch was removed from repository)
%td
= hidden_field_tag "allowed_to_merge_#{protected_branch.id}", protected_branch.merge_access_level.access_level
- = dropdown_tag(protected_branch.merge_access_level.humanize,
- options: { title: "Allowed to merge", toggle_class: 'allowed-to-merge', dropdown_class: 'dropdown-menu-selectable merge',
- data: { field_name: "allowed_to_merge_#{protected_branch.id}", url: url, id: protected_branch.id, type: "merge_access_level" }})
+ = dropdown_tag( (protected_branch.merge_access_level.humanize || 'Select') ,
+ options: { toggle_class: 'js-allowed-to-merge', dropdown_class: 'dropdown-menu-selectable js-allowed-to-merge-container',
+ data: { field_name: "allowed_to_merge_#{protected_branch.id}" }})
%td
= hidden_field_tag "allowed_to_push_#{protected_branch.id}", protected_branch.push_access_level.access_level
- = dropdown_tag(protected_branch.push_access_level.humanize,
- options: { title: "Allowed to push", toggle_class: 'allowed-to-push', dropdown_class: 'dropdown-menu-selectable push',
- data: { field_name: "allowed_to_push_#{protected_branch.id}", url: url, id: protected_branch.id, type: "push_access_level" }})
+ = dropdown_tag( (protected_branch.push_access_level.humanize || 'Select') ,
+ options: { toggle_class: 'js-allowed-to-push', dropdown_class: 'dropdown-menu-selectable js-allowed-to-push-container',
+ data: { field_name: "allowed_to_push_#{protected_branch.id}" }})
- if can_admin_project
%td
- = link_to 'Unprotect', [@project.namespace.becomes(Namespace), @project, protected_branch], data: { confirm: 'Branch will be writable for developers. Are you sure?' }, method: :delete, class: "btn btn-warning btn-sm pull-right"
+ = link_to 'Unprotect', [@project.namespace.becomes(Namespace), @project, protected_branch], data: { confirm: 'Branch will be writable for developers. Are you sure?' }, method: :delete, class: 'btn btn-warning'
diff --git a/app/views/projects/protected_branches/index.html.haml b/app/views/projects/protected_branches/index.html.haml
index 4efe44c7233..49dcc9a6ba4 100644
--- a/app/views/projects/protected_branches/index.html.haml
+++ b/app/views/projects/protected_branches/index.html.haml
@@ -14,41 +14,7 @@
%li prevent <strong>anyone</strong> from deleting the branch
%p.append-bottom-0 Read more about #{link_to "protected branches", help_page_path("user/project/protected_branches"), class: "underlined-link"} and #{link_to "project permissions", help_page_path("user/permissions"), class: "underlined-link"}.
.col-lg-9
- %h5.prepend-top-0
- Protect a branch
- if can? current_user, :admin_project, @project
- = form_for [@project.namespace.becomes(Namespace), @project, @protected_branch] do |f|
- = form_errors(@protected_branch)
+ = render 'create_protected_branch'
- .form-group
- = f.label :name, "Branch", class: "label-light"
- = render partial: "dropdown", locals: { f: f }
- %p.help-block
- = link_to "Wildcards", help_page_path('user/project/protected_branches', anchor: "wildcard-protected-branches")
- such as
- %code *-stable
- or
- %code production/*
- are supported.
-
- .form-group
- = hidden_field_tag 'protected_branch[merge_access_level_attributes][access_level]'
- = label_tag "Allowed to merge: ", nil, class: "label-light append-bottom-0"
- = dropdown_tag("<Make a selection>",
- options: { title: "Allowed to merge", toggle_class: 'allowed-to-merge',
- dropdown_class: 'dropdown-menu-selectable',
- data: { field_name: "protected_branch[merge_access_level_attributes][access_level]" }})
-
- .form-group
- = hidden_field_tag 'protected_branch[push_access_level_attributes][access_level]'
- = label_tag "Allowed to push: ", nil, class: "label-light append-bottom-0"
- = dropdown_tag("<Make a selection>",
- options: { title: "Allowed to push", toggle_class: 'allowed-to-push',
- dropdown_class: 'dropdown-menu-selectable',
- data: { field_name: "protected_branch[push_access_level_attributes][access_level]" }})
-
-
- = f.submit "Protect", class: "btn-create btn protect-branch-btn", disabled: true
-
- %hr
= render "branches_list"
diff --git a/app/views/shared/web_hooks/_form.html.haml b/app/views/shared/web_hooks/_form.html.haml
index 2585ed9360b..470dac6d75b 100644
--- a/app/views/shared/web_hooks/_form.html.haml
+++ b/app/views/shared/web_hooks/_form.html.haml
@@ -19,7 +19,7 @@
= f.label :token, "Secret Token", class: 'label-light'
= f.text_field :token, class: "form-control", placeholder: ''
%p.help-block
- Use this token to validate received payloads
+ Use this token to validate received payloads. It will be sent with the request in the X-Gitlab-Token HTTP header.
.form-group
= f.label :url, "Trigger", class: 'label-light'
%ul.list-unstyled
diff --git a/app/workers/post_receive.rb b/app/workers/post_receive.rb
index 09035a7cf2d..a9a2b716005 100644
--- a/app/workers/post_receive.rb
+++ b/app/workers/post_receive.rb
@@ -10,6 +10,10 @@ class PostReceive
log("Check gitlab.yml config for correct repositories.storages values. No repository storage path matches \"#{repo_path}\"")
end
+ changes = Base64.decode64(changes) unless changes.include?(' ')
+ # Use Sidekiq.logger so arguments can be correlated with execution
+ # time and thread ID's.
+ Sidekiq.logger.info "changes: #{changes.inspect}" if ENV['SIDEKIQ_LOG_ARGUMENTS']
post_received = Gitlab::GitPostReceive.new(repo_path, identifier, changes)
if post_received.project.nil?