Commit bcd096f1 authored by Alejandro Rodríguez's avatar Alejandro Rodríguez

Merge branch 'master' into 8-14-stable

parents e3e9cab0 4d59a9e7
CHANGELOG.md merge=union
*.js.es6 gitlab-language=javascript *.js.es6 gitlab-language=javascript
...@@ -4,37 +4,6 @@ entry. ...@@ -4,37 +4,6 @@ entry.
## 8.14.0 (2016-11-22) ## 8.14.0 (2016-11-22)
- Use separate email-token for incoming email and revert back the inactive feature. !5914
- Replace jQuery.timeago with timeago.js. !6274 (ClemMakesApps)
- Add CI notifications. Who triggered a pipeline would receive an email after the pipeline is succeeded or failed. Users could also update notification settings accordingly. !6342
- Finer-grained Git gargage collection. !6588
- Introduce better credential and error checking to `rake gitlab:ldap:check`. !6601
- Process commits using a dedicated Sidekiq worker. !6802
- Fix showing pipeline status for a given commit from correct branch. !7034
- Add query param to filter users by external & blocked type. !7109 (Yatish Mehta)
- Issues atom feed url reflect filters on dashboard. !7114 (Lucas Deschamps)
- Add setting to only allow merge requests to be merged when all discussions are resolved. !7125 (Rodolfo Arruda)
- Remove an extra leading space from diff paste data. !7133 (Hiroyuki Sato)
- Fix 404 on network page when entering non-existent git revision. !7172 (Hiroyuki Sato)
- Rewrite git blame spinach feature tests to rspec feature tests. !7197 (Lisanne Fellinger)
- Only skip group when it's actually a group in the "Share with group" select. !7262
- Introduce round-robin project creation to spread load over multiple shards. !7266
- Ensure merge request's "remove branch" accessors return booleans. !7267
- Expose label IDs in API. !7275 (Rares Sfirlogea)
- Fix invalid filename validation on eslint. !7281
- API: Ability to retrieve version information. !7286 (Robert Schilling)
- Set default Sidekiq retries to 3. !7294
- Return 400 when creating a system hook fails. !7350 (Robert Schilling)
- Use the Gitlab Workhorse HTTP header in the admin dashboard. (Chris Wright)
- Add an index for project_id in project_import_data to improve performance.
- Fix broken link to observatory cli on Frontend Dev Guide. (Sam Rose)
- Faster search inside Project.
- Clicking "force remove source branch" label now toggles the checkbox again.
- Allow to test JIRA service settings without having a repository.
- Fix: Guest sees some repository details and gets 404.
- Bump omniauth-gitlab to 1.0.2 to fix incompatibility with omniauth-oauth2.
- Fix: Todos Filter Shows All Users.
- Fix broken commits search.
- Show correct environment log in admin/logs (@duk3luk3 !7191) - Show correct environment log in admin/logs (@duk3luk3 !7191)
- Fix Milestone dropdown not stay selected for `Upcoming` and `No Milestone` option !7117 - Fix Milestone dropdown not stay selected for `Upcoming` and `No Milestone` option !7117
- Diff collapse won't shift when collapsing. - Diff collapse won't shift when collapsing.
...@@ -104,6 +73,7 @@ entry. ...@@ -104,6 +73,7 @@ entry.
- Fix applying GitHub-imported labels when importing job is interrupted - Fix applying GitHub-imported labels when importing job is interrupted
- Allow to search for user by secondary email address in the admin interface(/admin/users) !7115 (YarNayar) - Allow to search for user by secondary email address in the admin interface(/admin/users) !7115 (YarNayar)
- Updated commit SHA styling on the branches page. - Updated commit SHA styling on the branches page.
- Fix "Without projects" filter. !6611 (Ben Bodenmiller)
- Fix 404 when visit /projects page - Fix 404 when visit /projects page
## 8.13.5 (2016-11-08) ## 8.13.5 (2016-11-08)
......
...@@ -9,8 +9,6 @@ ...@@ -9,8 +9,6 @@
- [Helping others](#helping-others) - [Helping others](#helping-others)
- [I want to contribute!](#i-want-to-contribute) - [I want to contribute!](#i-want-to-contribute)
- [Implement design & UI elements](#implement-design-ui-elements) - [Implement design & UI elements](#implement-design-ui-elements)
- [Design reference](#design-reference)
- [UI development kit](#ui-development-kit)
- [Issue tracker](#issue-tracker) - [Issue tracker](#issue-tracker)
- [Feature proposals](#feature-proposals) - [Feature proposals](#feature-proposals)
- [Issue tracker guidelines](#issue-tracker-guidelines) - [Issue tracker guidelines](#issue-tracker-guidelines)
...@@ -90,7 +88,7 @@ This was inspired by [an article by Kent C. Dodds][medium-up-for-grabs]. ...@@ -90,7 +88,7 @@ This was inspired by [an article by Kent C. Dodds][medium-up-for-grabs].
## Implement design & UI elements ## Implement design & UI elements
Please see the [UI Guide for building GitLab]. Please see the [UX Guide for GitLab].
## Issue tracker ## Issue tracker
...@@ -469,5 +467,5 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor ...@@ -469,5 +467,5 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor
[doc-styleguide]: doc/development/doc_styleguide.md "Documentation styleguide" [doc-styleguide]: doc/development/doc_styleguide.md "Documentation styleguide"
[scss-styleguide]: doc/development/scss_styleguide.md "SCSS styleguide" [scss-styleguide]: doc/development/scss_styleguide.md "SCSS styleguide"
[newlines-styleguide]: doc/development/newlines_styleguide.md "Newlines styleguide" [newlines-styleguide]: doc/development/newlines_styleguide.md "Newlines styleguide"
[UI Guide for building GitLab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/development/ui_guide.md [UX Guide for GitLab]: http://docs.gitlab.com/ce/development/ux_guide/
[license-finder-doc]: doc/development/licensing.md [license-finder-doc]: doc/development/licensing.md
...@@ -53,6 +53,7 @@ ...@@ -53,6 +53,7 @@
/*= require_directory ./u2f */ /*= require_directory ./u2f */
/*= require_directory . */ /*= require_directory . */
/*= require fuzzaldrin-plus */ /*= require fuzzaldrin-plus */
/*= require es6-promise.auto */
(function () { (function () {
document.addEventListener('page:fetch', gl.utils.cleanupBeforeFetch); document.addEventListener('page:fetch', gl.utils.cleanupBeforeFetch);
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
}, },
// Team Members // Team Members
Members: { Members: {
template: '<li>${username} <small>${title}</small></li>' template: '<li>${avatarTag} ${username} <small>${title}</small></li>'
}, },
Labels: { Labels: {
template: '<li><span class="dropdown-label-box" style="background: ${color}"></span> ${title}</li>' template: '<li><span class="dropdown-label-box" style="background: ${color}"></span> ${title}</li>'
...@@ -51,6 +51,11 @@ ...@@ -51,6 +51,11 @@
if (!GitLab.GfmAutoComplete.dataLoaded) { if (!GitLab.GfmAutoComplete.dataLoaded) {
return this.at; return this.at;
} else { } else {
if (value.indexOf("unlabel") !== -1) {
GitLab.GfmAutoComplete.input.atwho('load', '~', GitLab.GfmAutoComplete.cachedData.unlabels);
} else {
GitLab.GfmAutoComplete.input.atwho('load', '~', GitLab.GfmAutoComplete.cachedData.labels);
}
return value; return value;
} }
} }
...@@ -118,7 +123,7 @@ ...@@ -118,7 +123,7 @@
beforeInsert: this.DefaultOptions.beforeInsert, beforeInsert: this.DefaultOptions.beforeInsert,
beforeSave: function(members) { beforeSave: function(members) {
return $.map(members, function(m) { return $.map(members, function(m) {
var title; let title = '';
if (m.username == null) { if (m.username == null) {
return m; return m;
} }
...@@ -126,8 +131,14 @@ ...@@ -126,8 +131,14 @@
if (m.count) { if (m.count) {
title += " (" + m.count + ")"; title += " (" + m.count + ")";
} }
const autoCompleteAvatar = m.avatar_url || m.username.charAt(0).toUpperCase();
const imgAvatar = `<img src="${m.avatar_url}" alt="${m.username}" class="avatar avatar-inline center s26"/>`;
const txtAvatar = `<div class="avatar center avatar-inline s26">${autoCompleteAvatar}</div>`;
return { return {
username: m.username, username: m.username,
avatarTag: autoCompleteAvatar.length === 1 ? txtAvatar : imgAvatar,
title: gl.utils.sanitize(title), title: gl.utils.sanitize(title),
search: gl.utils.sanitize(m.username + " " + m.name) search: gl.utils.sanitize(m.username + " " + m.name)
}; };
...@@ -352,3 +363,4 @@ ...@@ -352,3 +363,4 @@
}; };
}).call(this); }).call(this);
...@@ -249,7 +249,7 @@ ...@@ -249,7 +249,7 @@
_this.fullData = data; _this.fullData = data;
_this.parseData(_this.fullData); _this.parseData(_this.fullData);
_this.focusTextInput(); _this.focusTextInput();
if (_this.options.filterable && _this.filter && _this.filter.input) { if (_this.options.filterable && _this.filter && _this.filter.input && _this.filter.input.val().trim() !== '') {
return _this.filter.input.trigger('input'); return _this.filter.input.trigger('input');
} }
}; };
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
&.avatar-inline { &.avatar-inline {
float: none; float: none;
display: inline-block;
margin-left: 4px; margin-left: 4px;
margin-bottom: 2px; margin-bottom: 2px;
...@@ -41,6 +42,12 @@ ...@@ -41,6 +42,12 @@
&.s24 { margin-right: 4px; } &.s24 { margin-right: 4px; }
} }
&.center {
font-size: 14px;
line-height: 1.8em;
text-align: center;
}
&.avatar-tile { &.avatar-tile {
border-radius: 0; border-radius: 0;
border: none; border: none;
......
...@@ -141,6 +141,10 @@ ...@@ -141,6 +141,10 @@
&.btn-save { &.btn-save {
@include btn-outline($white-light, $green-normal, $green-normal, $green-light, $white-light, $green-light); @include btn-outline($white-light, $green-normal, $green-normal, $green-light, $white-light, $green-light);
} }
&.btn-remove {
@include btn-outline($white-light, $red-normal, $red-normal, $red-light, $white-light, $red-light);
}
} }
&.btn-gray { &.btn-gray {
......
...@@ -63,7 +63,11 @@ header { ...@@ -63,7 +63,11 @@ header {
&:focus, &:focus,
&:active { &:active {
background-color: $background-color; background-color: $background-color;
color: darken($gl-icon-color, 50%); color: darken($gl-icon-color, 30%);
.todos-pending-count {
background: darken($todo-alert-blue, 10%);
}
} }
.fa-caret-down { .fa-caret-down {
...@@ -194,7 +198,7 @@ header { ...@@ -194,7 +198,7 @@ header {
cursor: pointer; cursor: pointer;
&:hover { &:hover {
color: darken($color: $gl-text-color, $amount: 50%); color: darken($color: $gl-text-color, $amount: 30%);
} }
} }
......
...@@ -148,7 +148,19 @@ ...@@ -148,7 +148,19 @@
} }
} }
.atwho-view small.description { .atwho-view {
small.description {
float: right; float: right;
padding: 3px 5px; padding: 3px 5px;
}
.avatar-inline {
margin-bottom: 0;
}
.cur {
.avatar {
border: 1px solid $white-light;
}
}
} }
\ No newline at end of file
...@@ -8,6 +8,10 @@ ...@@ -8,6 +8,10 @@
border-bottom: none; border-bottom: none;
} }
} }
.blob-result {
margin: 5px 0;
}
} }
.search { .search {
...@@ -157,7 +161,6 @@ ...@@ -157,7 +161,6 @@
width: 68%; width: 68%;
} }
} }
} }
.search-holder { .search-holder {
...@@ -234,5 +237,4 @@ ...@@ -234,5 +237,4 @@
&:focus { &:focus {
color: $gl-link-color; color: $gl-link-color;
} }
} }
...@@ -4,7 +4,7 @@ class Projects::BranchesController < Projects::ApplicationController ...@@ -4,7 +4,7 @@ class Projects::BranchesController < Projects::ApplicationController
# Authorize # Authorize
before_action :require_non_empty_project before_action :require_non_empty_project
before_action :authorize_download_code! before_action :authorize_download_code!
before_action :authorize_push_code!, only: [:new, :create, :destroy] before_action :authorize_push_code!, only: [:new, :create, :destroy, :destroy_all_merged]
def index def index
@sort = params[:sort].presence || sort_value_name @sort = params[:sort].presence || sort_value_name
...@@ -62,6 +62,13 @@ class Projects::BranchesController < Projects::ApplicationController ...@@ -62,6 +62,13 @@ class Projects::BranchesController < Projects::ApplicationController
end end
end end
def destroy_all_merged
DeleteMergedBranchesService.new(@project, current_user).async_execute
redirect_to namespace_project_branches_path(@project.namespace, @project),
notice: 'Merged branches are being deleted. This can take some time depending on the number of branches. Please refresh the page to see changes.'
end
private private
def ref def ref
......
...@@ -31,10 +31,6 @@ class Projects::LfsApiController < Projects::GitHttpClientController ...@@ -31,10 +31,6 @@ class Projects::LfsApiController < Projects::GitHttpClientController
private private
def objects
@objects ||= (params[:objects] || []).to_a
end
def existing_oids def existing_oids
@existing_oids ||= begin @existing_oids ||= begin
storage_project.lfs_objects.where(oid: objects.map { |o| o['oid'].to_s }).pluck(:oid) storage_project.lfs_objects.where(oid: objects.map { |o| o['oid'].to_s }).pluck(:oid)
......
...@@ -10,8 +10,7 @@ class Projects::ServicesController < Projects::ApplicationController ...@@ -10,8 +10,7 @@ class Projects::ServicesController < Projects::ApplicationController
layout "project_settings" layout "project_settings"
def index def index
@project.build_missing_services @services = @project.find_or_initialize_services
@services = @project.services.visible.reload
end end
def edit def edit
...@@ -46,6 +45,6 @@ class Projects::ServicesController < Projects::ApplicationController ...@@ -46,6 +45,6 @@ class Projects::ServicesController < Projects::ApplicationController
private private
def service def service
@service ||= @project.services.find { |service| service.to_param == params[:id] } @service ||= @project.find_or_initialize_service(params[:id])
end end
end end
...@@ -144,13 +144,15 @@ class ProjectsController < Projects::ApplicationController ...@@ -144,13 +144,15 @@ class ProjectsController < Projects::ApplicationController
autocomplete = ::Projects::AutocompleteService.new(@project, current_user) autocomplete = ::Projects::AutocompleteService.new(@project, current_user)
participants = ::Projects::ParticipantsService.new(@project, current_user).execute(noteable) participants = ::Projects::ParticipantsService.new(@project, current_user).execute(noteable)
unlabels = autocomplete.unlabels(noteable)
@suggestions = { @suggestions = {
emojis: Gitlab::AwardEmoji.urls, emojis: Gitlab::AwardEmoji.urls,
issues: autocomplete.issues, issues: autocomplete.issues,
milestones: autocomplete.milestones, milestones: autocomplete.milestones,
mergerequests: autocomplete.merge_requests, mergerequests: autocomplete.merge_requests,
labels: autocomplete.labels, labels: autocomplete.labels - unlabels,
unlabels: unlabels,
members: participants, members: participants,
commands: autocomplete.commands(noteable, params[:type]) commands: autocomplete.commands(noteable, params[:type])
} }
......
...@@ -6,7 +6,7 @@ class LabelsFinder < UnionFinder ...@@ -6,7 +6,7 @@ class LabelsFinder < UnionFinder
def execute(skip_authorization: false) def execute(skip_authorization: false)
@skip_authorization = skip_authorization @skip_authorization = skip_authorization
items = find_union(label_ids, Label) items = find_union(label_ids, Label) || Label.none
items = with_title(items) items = with_title(items)
sort(items) sort(items)
end end
...@@ -18,9 +18,11 @@ class LabelsFinder < UnionFinder ...@@ -18,9 +18,11 @@ class LabelsFinder < UnionFinder
def label_ids def label_ids
label_ids = [] label_ids = []
if project?
if project if project
label_ids << project.group.labels if project.group.present? label_ids << project.group.labels if project.group.present?
label_ids << project.labels label_ids << project.labels
end
else else
label_ids << Label.where(group_id: projects.group_ids) label_ids << Label.where(group_id: projects.group_ids)
label_ids << Label.where(project_id: projects.select(:id)) label_ids << Label.where(project_id: projects.select(:id))
...@@ -40,16 +42,16 @@ class LabelsFinder < UnionFinder ...@@ -40,16 +42,16 @@ class LabelsFinder < UnionFinder
items.where(title: title) items.where(title: title)
end end
def group_id def group?
params[:group_id].presence params[:group_id].present?
end end
def project_id def project?
params[:project_id].presence params[:project_id].present?
end end
def projects_ids def projects?
params[:project_ids] params[:project_ids].present?
end end
def title def title
...@@ -59,8 +61,9 @@ class LabelsFinder < UnionFinder ...@@ -59,8 +61,9 @@ class LabelsFinder < UnionFinder
def project def project
return @project if defined?(@project) return @project if defined?(@project)
if project_id if project?
@project = find_project @project = Project.find(params[:project_id])
@project = nil unless authorized_to_read_labels?(@project)
else else
@project = nil @project = nil
end end
...@@ -68,26 +71,20 @@ class LabelsFinder < UnionFinder ...@@ -68,26 +71,20 @@ class LabelsFinder < UnionFinder
@project @project
end end
def find_project
if skip_authorization
Project.find_by(id: project_id)
else
available_projects.find_by(id: project_id)
end
end
def projects def projects
return @projects if defined?(@projects) return @projects if defined?(@projects)
@projects = skip_authorization ? Project.all : available_projects @projects = skip_authorization ? Project.all : ProjectsFinder.new.execute(current_user)
@projects = @projects.in_namespace(group_id) if group_id @projects = @projects.in_namespace(params[:group_id]) if group?
@projects = @projects.where(id: projects_ids) if projects_ids @projects = @projects.where(id: params[:project_ids]) if projects?
@projects = @projects.reorder(nil) @projects = @projects.reorder(nil)
@projects @projects
end end
def available_projects def authorized_to_read_labels?(project)
@available_projects ||= ProjectsFinder.new.execute(current_user) return true if skip_authorization
Ability.allowed?(current_user, :read_label, project)
end end
end end
...@@ -86,7 +86,7 @@ module EventsHelper ...@@ -86,7 +86,7 @@ module EventsHelper
elsif event.merge_request? elsif event.merge_request?
namespace_project_merge_request_url(event.project.namespace, namespace_project_merge_request_url(event.project.namespace,
event.project, event.merge_request) event.project, event.merge_request)
elsif event.note? && event.commit_note? elsif event.commit_note?
namespace_project_commit_url(event.project.namespace, event.project, namespace_project_commit_url(event.project.namespace, event.project,
event.note_target) event.note_target)
elsif event.note? elsif event.note?
...@@ -127,7 +127,7 @@ module EventsHelper ...@@ -127,7 +127,7 @@ module EventsHelper
end end
def event_note_target_path(event) def event_note_target_path(event)
if event.note? && event.commit_note? if event.commit_note?
namespace_project_commit_path(event.project.namespace, namespace_project_commit_path(event.project.namespace,
event.project, event.project,
event.note_target, event.note_target,
......
...@@ -30,11 +30,6 @@ module IssuablesHelper ...@@ -30,11 +30,6 @@ module IssuablesHelper
end end
end end
def can_add_template?(issuable)
names = issuable_templates(issuable)
names.empty? && can?(current_user, :push_code, @project) && !@project.private?
end
def template_dropdown_tag(issuable, &block) def template_dropdown_tag(issuable, &block)
title = selected_template(issuable) || "Choose a template" title = selected_template(issuable) || "Choose a template"
options = { options = {
...@@ -141,8 +136,19 @@ module IssuablesHelper ...@@ -141,8 +136,19 @@ module IssuablesHelper
html.html_safe html.html_safe
end end
def cached_assigned_issuables_count(assignee, issuable_type, state)
cache_key = hexdigest(['assigned_issuables_count', assignee.id, issuable_type, state].join('-'))
Rails.cache.fetch(cache_key, expires_in: 2.minutes) do
assigned_issuables_count(assignee, issuable_type, state)
end
end
private private
def assigned_issuables_count(assignee, issuable_type, state)
assignee.public_send("assigned_#{issuable_type}").public_send(state).count
end
def sidebar_gutter_collapsed? def sidebar_gutter_collapsed?
cookies[:collapsed_gutter] == 'true' cookies[:collapsed_gutter] == 'true'
end end
......
...@@ -30,6 +30,10 @@ module LfsHelper ...@@ -30,6 +30,10 @@ module LfsHelper
ci? || lfs_deploy_token? || user_can_download_code? || build_can_download_code? ci? || lfs_deploy_token? || user_can_download_code? || build_can_download_code?
end end
def objects
@objects ||= (params[:objects] || []).to_a
end
def user_can_download_code? def user_can_download_code?
has_authentication_ability?(:download_code) && can?(user, :download_code, project) has_authentication_ability?(:download_code) && can?(user, :download_code, project)
end end
......
...@@ -31,34 +31,7 @@ module SearchHelper ...@@ -31,34 +31,7 @@ module SearchHelper
end end
def parse_search_result(result) def parse_search_result(result)
ref = nil Gitlab::ProjectSearchResults.parse_search_result(result)
filename = nil
basename = nil
startline = 0
result.each_line.each_with_index do |line, index|
if line =~ /^.*:.*:\d+:/
ref, filename, startline = line.split(':')
startline = startline.to_i - index
extname = Regexp.escape(File.extname(filename))
basename = filename.sub(/#{extname}$/, '')
break
end
end
data = ""
result.each_line do |line|
data << line.sub(ref, '').sub(filename, '').sub(/^:-\d+-/, '').sub(/^::\d+:/, '')
end
OpenStruct.new(
filename: filename,
basename: basename,
ref: ref,
startline: startline,
data: data
)
end end
private private
......
module TriggersHelper module TriggersHelper
def builds_trigger_url(project_id) def builds_trigger_url(project_id, ref: nil)
if ref.nil?
"#{Settings.gitlab.url}/api/v3/projects/#{project_id}/trigger/builds" "#{Settings.gitlab.url}/api/v3/projects/#{project_id}/trigger/builds"
else
"#{Settings.gitlab.url}/api/v3/projects/#{project_id}/ref/#{ref}/trigger/builds"
end
end end
end end
...@@ -62,7 +62,7 @@ class Event < ActiveRecord::Base ...@@ -62,7 +62,7 @@ class Event < ActiveRecord::Base
end end
def visible_to_user?(user = nil) def visible_to_user?(user = nil)
if push? if push? || commit_note?
Ability.allowed?(user, :download_code, project) Ability.allowed?(user, :download_code, project)
elsif membership_changed? elsif membership_changed?
true true
...@@ -283,7 +283,7 @@ class Event < ActiveRecord::Base ...@@ -283,7 +283,7 @@ class Event < ActiveRecord::Base
end end
def commit_note? def commit_note?
target.for_commit? note? && target && target.for_commit?
end end
def issue_note? def issue_note?
...@@ -295,7 +295,7 @@ class Event < ActiveRecord::Base ...@@ -295,7 +295,7 @@ class Event < ActiveRecord::Base
end end
def project_snippet_note? def project_snippet_note?
target.for_snippet? note? && target && target.for_snippet?
end end
def note_target def note_target
......
...@@ -6,7 +6,7 @@ class Key < ActiveRecord::Base ...@@ -6,7 +6,7 @@ class Key < ActiveRecord::Base
belongs_to :user belongs_to :user
before_validation :strip_white_space, :generate_fingerprint before_validation :generate_fingerprint
validates :title, presence: true, length: { within: 0..255 } validates :title, presence: true, length: { within: 0..255 }
validates :key, presence: true, length: { within: 0..5000 }, format: { with: /\A(ssh|ecdsa)-.*\Z/ } validates :key, presence: true, length: { within: 0..5000 }, format: { with: /\A(ssh|ecdsa)-.*\Z/ }
...@@ -21,8 +21,9 @@ class Key < ActiveRecord::Base ...@@ -21,8 +21,9 @@ class Key < ActiveRecord::Base
after_destroy :remove_from_shell after_destroy :remove_from_shell
after_destroy :post_destroy_hook after_destroy :post_destroy_hook
def strip_white_space def key=(value)
self.key = key.strip unless key.blank? value.strip! unless value.blank?
write_attribute(:key, value)
end end
def publishable_key def publishable_key
......
...@@ -35,6 +35,7 @@ class Project < ActiveRecord::Base ...@@ -35,6 +35,7 @@ class Project < ActiveRecord::Base
default_value_for :builds_enabled, gitlab_config_features.builds default_value_for :builds_enabled, gitlab_config_features.builds
default_value_for :wiki_enabled, gitlab_config_features.wiki default_value_for :wiki_enabled, gitlab_config_features.wiki
default_value_for :snippets_enabled, gitlab_config_features.snippets default_value_for :snippets_enabled, gitlab_config_features.snippets
default_value_for :only_allow_merge_if_all_discussions_are_resolved, false
after_create :ensure_dir_exist after_create :ensure_dir_exist
after_create :create_project_feature, unless: :project_feature after_create :create_project_feature, unless: :project_feature
...@@ -76,7 +77,6 @@ class Project < ActiveRecord::Base ...@@ -76,7 +77,6 @@ class Project < ActiveRecord::Base
has_many :boards, before_add: :validate_board_limit, dependent: :destroy has_many :boards, before_add: :validate_board_limit, dependent: :destroy
# Project services # Project services
has_many :services
has_one :campfire_service, dependent: :destroy has_one :campfire_service, dependent: :destroy
has_one :drone_ci_service, dependent: :destroy has_one :drone_ci_service, dependent: :destroy
has_one :emails_on_push_service, dependent: :destroy has_one :emails_on_push_service, dependent: :destroy
...@@ -748,25 +748,30 @@ class Project < ActiveRecord::Base ...@@ -748,25 +748,30 @@ class Project < ActiveRecord::Base
update_column(:has_external_wiki, services.external_wikis.any?) update_column(:has_external_wiki, services.external_wikis.any?)
end end
def build_missing_services def find_or_initialize_services
services_templates = Service.where(template: true) services_templates = Service.where(template: true)
Service.available_services_names.each do |service_name| Service.available_services_names.map do |service_name|
service = find_service(services, service_name) service = find_service(services, service_name)
# If service is available but missing in db if service
if service.nil? service
else
# We should check if template for the service exists # We should check if template for the service exists
template = find_service(services_templates, service_name) template = find_service(services_templates, service_name)
if template.nil? if template.nil?
# If no template, we should create an instance. Ex `create_gitlab_ci_service` # If no template, we should create an instance. Ex `build_gitlab_ci_service`
public_send("create_#{service_name}_service") public_send("build_#{service_name}_service")
else else
Service.create_from_template(self.id, template) Service.build_from_template(id, template)
end
end end
end end
end end
def find_or_initialize_service(name)
find_or_initialize_services.find { |service| service.to_param == name }
end end
def create_labels def create_labels
...@@ -878,7 +883,7 @@ class Project < ActiveRecord::Base ...@@ -878,7 +883,7 @@ class Project < ActiveRecord::Base
end end
def empty_repo? def empty_repo?
!repository.exists? || !repository.has_visible_content? repository.empty_repo?
end end
def repo def repo
...@@ -1334,10 +1339,6 @@ class Project < ActiveRecord::Base ...@@ -1334,10 +1339,6 @@ class Project < ActiveRecord::Base
end end
end end
def only_allow_merge_if_all_discussions_are_resolved
super || false
end
private private
def pushes_since_gc_redis_key def pushes_since_gc_redis_key
......
...@@ -127,7 +127,7 @@ class ProjectWiki ...@@ -127,7 +127,7 @@ class ProjectWiki
end end
def search_files(query) def search_files(query)
repository.search_files(query, default_branch) repository.search_files_by_content(query, default_branch)
end end
def repository def repository
......
...@@ -15,16 +15,6 @@ class Repository ...@@ -15,16 +15,6 @@ class Repository
Gitlab.config.repositories.storages Gitlab.config.repositories.storages
end end
def self.remove_storage_from_path(repo_path)
storages.find do |_, storage_path|
if repo_path.start_with?(storage_path)
return repo_path.sub(storage_path, '')
end
end
repo_path
end
def initialize(path_with_namespace, project) def initialize(path_with_namespace, project)
@path_with_namespace = path_with_namespace @path_with_namespace = path_with_namespace
@project = project @project = project
...@@ -631,7 +621,7 @@ class Repository ...@@ -631,7 +621,7 @@ class Repository
@head_tree ||= Tree.new(self, head_commit.sha, nil) @head_tree ||= Tree.new(self, head_commit.sha, nil)
end end
def tree(sha = :head, path = nil) def tree(sha = :head, path = nil, recursive: false)
if sha == :head if sha == :head
if path.nil? if path.nil?
return head_tree return head_tree
...@@ -640,7 +630,7 @@ class Repository ...@@ -640,7 +630,7 @@ class Repository
end end
end end
Tree.new(self, sha, path) Tree.new(self, sha, path, recursive: recursive)
end end
def blob_at_branch(branch_name, path) def blob_at_branch(branch_name, path)
...@@ -1063,16 +1053,25 @@ class Repository ...@@ -1063,16 +1053,25 @@ class Repository
merge_base(ancestor_id, descendant_id) == ancestor_id merge_base(ancestor_id, descendant_id) == ancestor_id
end end
def search_files(query, ref) def empty_repo?
unless exists? && has_visible_content? && query.present? !exists? || !has_visible_content?
return []
end end
def search_files_by_content(query, ref)
return [] if empty_repo? || query.blank?
offset = 2 offset = 2
args = %W(#{Gitlab.config.git.bin_path} grep -i -I -n --before-context #{offset} --after-context #{offset} -E -e #{Regexp.escape(query)} #{ref || root_ref}) args = %W(#{Gitlab.config.git.bin_path} grep -i -I -n --before-context #{offset} --after-context #{offset} -E -e #{Regexp.escape(query)} #{ref || root_ref})
Gitlab::Popen.popen(args, path_to_repo).first.scrub.split(/^--$/) Gitlab::Popen.popen(args, path_to_repo).first.scrub.split(/^--$/)
end end
def search_files_by_name(query, ref)
return [] if empty_repo? || query.blank?
args = %W(#{Gitlab.config.git.bin_path} ls-tree --full-tree -r #{ref || root_ref} --name-status | #{Regexp.escape(query)})
Gitlab::Popen.popen(args, path_to_repo).first.lines.map(&:strip)
end
def fetch_ref(source_path, source_ref, target_ref) def fetch_ref(source_path, source_ref, target_ref)
args = %W(#{Gitlab.config.git.bin_path} fetch --no-tags -f #{source_path} #{source_ref}:#{target_ref}) args = %W(#{Gitlab.config.git.bin_path} fetch --no-tags -f #{source_path} #{source_ref}:#{target_ref})
Gitlab::Popen.popen(args, path_to_repo) Gitlab::Popen.popen(args, path_to_repo)
......
...@@ -222,11 +222,11 @@ class Service < ActiveRecord::Base ...@@ -222,11 +222,11 @@ class Service < ActiveRecord::Base
] ]
end end
def self.create_from_template(project_id, template) def self.build_from_template(project_id, template)
service = template.dup service = template.dup
service.template = false service.template = false
service.project_id = project_id service.project_id = project_id
service if service.save service
end end
private private
......
...@@ -3,15 +3,16 @@ class Tree ...@@ -3,15 +3,16 @@ class Tree
attr_accessor :repository, :sha, :path, :entries attr_accessor :repository, :sha, :path, :entries
def initialize(repository, sha, path = '/') def initialize(repository, sha, path = '/', recursive: false)
path = '/' if path.blank? path = '/' if path.blank?
@repository = repository @repository = repository
@sha = sha @sha = sha
@path = path @path = path
@recursive = recursive
git_repo = @repository.raw_repository git_repo = @repository.raw_repository
@entries = Gitlab::Git::Tree.where(git_repo, @sha, @path) @entries = get_entries(git_repo, @sha, @path, recursive: @recursive)
end end
def readme def readme
...@@ -58,4 +59,21 @@ class Tree ...@@ -58,4 +59,21 @@ class Tree
def sorted_entries def sorted_entries
trees + blobs + submodules trees + blobs + submodules
end end
private
def get_entries(git_repo, sha, path, recursive: false)
current_path_entries = Gitlab::Git::Tree.where(git_repo, sha, path)
ordered_entries = []
current_path_entries.each do |entry|
ordered_entries << entry
if recursive && entry.dir?
ordered_entries.concat(get_entries(git_repo, sha, entry.path, recursive: true))
end
end
ordered_entries
end
end end
...@@ -173,7 +173,7 @@ class User < ActiveRecord::Base ...@@ -173,7 +173,7 @@ class User < ActiveRecord::Base
scope :external, -> { where(external: true) } scope :external, -> { where(external: true) }
scope :active, -> { with_state(:active) } scope :active, -> { with_state(:active) }
scope :not_in_project, ->(project) { project.users.present? ? where("id not in (:ids)", ids: project.users.map(&:id) ) : all } scope :not_in_project, ->(project) { project.users.present? ? where("id not in (:ids)", ids: project.users.map(&:id) ) : all }
scope :without_projects, -> { where('id NOT IN (SELECT DISTINCT(user_id) FROM members)') } scope :without_projects, -> { where('id NOT IN (SELECT DISTINCT(user_id) FROM members WHERE user_id IS NOT NULL AND requested_at IS NULL)') }
scope :todo_authors, ->(user_id, state) { where(id: Todo.where(user_id: user_id, state: state).select(:author_id)) } scope :todo_authors, ->(user_id, state) { where(id: Todo.where(user_id: user_id, state: state).select(:author_id)) }
def self.with_two_factor def self.with_two_factor
......
require_relative 'base_service'
class DeleteMergedBranchesService < BaseService
def async_execute
DeleteMergedBranchesWorker.perform_async(project.id, current_user.id)
end
def execute
raise Gitlab::Access::AccessDeniedError unless can?(current_user, :push_code, project)
branches = project.repository.branch_names
branches = branches.select { |branch| project.repository.merged_to_root_ref?(branch) }
branches.each do |branch|
DeleteBranchService.new(project, current_user).execute(branch)
end
end
end
...@@ -60,15 +60,7 @@ module MergeRequests ...@@ -60,15 +60,7 @@ module MergeRequests
merge_requests = filter_merge_requests(merge_requests) merge_requests = filter_merge_requests(merge_requests)
merge_requests.each do |merge_request| merge_requests.each do |merge_request|
if merge_request.source_branch == @branch_name || force_push? reload_diff(merge_request) unless branch_removed?
merge_request.reload_diff
else
mr_commit_ids = merge_request.commits.map(&:id)
push_commit_ids = @commits.map(&:id)
matches = mr_commit_ids & push_commit_ids
merge_request.reload_diff if matches.any?
end
merge_request.mark_as_unchecked merge_request.mark_as_unchecked
end end
end end
...@@ -173,5 +165,16 @@ module MergeRequests ...@@ -173,5 +165,16 @@ module MergeRequests
def branch_removed? def branch_removed?
Gitlab::Git.blank_ref?(@newrev) Gitlab::Git.blank_ref?(@newrev)
end end
def reload_diff(merge_request)
if merge_request.source_branch == @branch_name || force_push?
merge_request.reload_diff
else
mr_commit_ids = merge_request.commits.map(&:id)
push_commit_ids = @commits.map(&:id)
matches = mr_commit_ids & push_commit_ids
merge_request.reload_diff if matches.any?
end
end
end end
end end
...@@ -13,7 +13,14 @@ module Projects ...@@ -13,7 +13,14 @@ module Projects
end end
def labels def labels
LabelsFinder.new(current_user, project_id: project.id).execute.select([:title, :color]) LabelsFinder.new(current_user, project_id: project.id).execute.
pluck(:title, :color).map { |l| { title: l.first, color: l.second } }
end
def unlabels(noteable)
return [] unless noteable && noteable.respond_to?(:labels)
noteable.labels.pluck(:title, :color).map { |l| { title: l.first, color: l.second } }
end end
def commands(noteable, type) def commands(noteable, type)
......
...@@ -95,7 +95,7 @@ module Projects ...@@ -95,7 +95,7 @@ module Projects
unless @project.gitlab_project_import? unless @project.gitlab_project_import?
@project.create_wiki unless skip_wiki? @project.create_wiki unless skip_wiki?
@project.build_missing_services create_services_from_active_templates(@project)
@project.create_labels @project.create_labels
end end
...@@ -135,5 +135,12 @@ module Projects ...@@ -135,5 +135,12 @@ module Projects
@project @project
end end
def create_services_from_active_templates(project)
Service.where(template: true, active: true).each do |template|
service = Service.build_from_template(project.id, template)
service.save!
end
end
end end
end end
...@@ -15,7 +15,8 @@ module Projects ...@@ -15,7 +15,8 @@ module Projects
[{ [{
name: noteable.author.name, name: noteable.author.name,
username: noteable.author.username username: noteable.author.username,
avatar_url: noteable.author.avatar_url
}] }]
end end
...@@ -28,14 +29,14 @@ module Projects ...@@ -28,14 +29,14 @@ module Projects
def sorted(users) def sorted(users)
users.uniq.to_a.compact.sort_by(&:username).map do |user| users.uniq.to_a.compact.sort_by(&:username).map do |user|
{ username: user.username, name: user.name } { username: user.username, name: user.name, avatar_url: user.avatar_url }
end end
end end
def groups def groups
current_user.authorized_groups.sort_by(&:path).map do |group| current_user.authorized_groups.sort_by(&:path).map do |group|
count = group.users.count count = group.users.count
{ username: group.path, name: group.name, count: count } { username: group.path, name: group.name, count: count, avatar_url: group.avatar.url }
end end
end end
......
...@@ -26,12 +26,12 @@ ...@@ -26,12 +26,12 @@
= link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues' do = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues' do
%span %span
Issues Issues
%span.count= number_with_delimiter(current_user.assigned_issues.opened.count) %span.count= number_with_delimiter(cached_assigned_issuables_count(current_user, :issues, :opened))
= nav_link(path: 'dashboard#merge_requests') do = nav_link(path: 'dashboard#merge_requests') do
= link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do = link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do
%span %span
Merge Requests Merge Requests
%span.count= number_with_delimiter(current_user.assigned_merge_requests.opened.count) %span.count= number_with_delimiter(cached_assigned_issuables_count(current_user, :merge_requests, :opened))
= nav_link(controller: 'dashboard/snippets') do = nav_link(controller: 'dashboard/snippets') do
= link_to dashboard_snippets_path, title: 'Snippets' do = link_to dashboard_snippets_path, title: 'Snippets' do
%span %span
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
data: { container: "body", placement: "bottom" } } data: { container: "body", placement: "bottom" } }
{{ list.title }} {{ list.title }}
.board-issue-count-holder.pull-right.clearfix{ "v-if" => 'list.type !== "blank"' } .board-issue-count-holder.pull-right.clearfix{ "v-if" => 'list.type !== "blank"' }
%span.board-issue-count.pull-left{ ":class" => '{ "has-btn": list.type !== "done" }' } %span.board-issue-count.pull-left{ ":class" => '{ "has-btn": list.type !== "done" && !disabled }' }
{{ list.issuesSize }} {{ list.issuesSize }}
- if can?(current_user, :admin_issue, @project) - if can?(current_user, :admin_issue, @project)
%button.btn.btn-small.btn-default.pull-right.has-tooltip{ type: "button", %button.btn.btn-small.btn-default.pull-right.has-tooltip{ type: "button",
......
...@@ -26,6 +26,8 @@ ...@@ -26,6 +26,8 @@
= sort_title_oldest_updated = sort_title_oldest_updated
- if can? current_user, :push_code, @project - if can? current_user, :push_code, @project
= link_to namespace_project_merged_branches_path(@project.namespace, @project), class: 'btn btn-inverted btn-remove has-tooltip', title: "Delete all branches that are merged into '#{@project.repository.root_ref}'", method: :delete, data: { confirm: "Deleting the merged branches cannot be undone. Are you sure?", container: 'body' } do
Delete merged branches
= link_to new_namespace_project_branch_path(@project.namespace, @project), class: 'btn btn-create' do = link_to new_namespace_project_branch_path(@project.namespace, @project), class: 'btn btn-create' do
New branch New branch
......
...@@ -25,7 +25,7 @@ ...@@ -25,7 +25,7 @@
- elsif diff_file.renamed_file - elsif diff_file.renamed_file
.nothing-here-block File moved .nothing-here-block File moved
- elsif blob.image? - elsif blob.image?
- old_blob = diff_file.old_blob(diff_commit) - old_blob = diff_file.old_blob(diff_file.old_content_commit || @base_commit)
= render "projects/diffs/image", diff_file: diff_file, old_file: old_blob, file: blob = render "projects/diffs/image", diff_file: diff_file, old_file: old_blob, file: blob
- else - else
.nothing-here-block No preview for this file type .nothing-here-block No preview for this file type
...@@ -28,5 +28,6 @@ ...@@ -28,5 +28,6 @@
%td.hidden-xs %td.hidden-xs
= service.description = service.description
%td.light %td.light
- if service.updated_at.present?
= time_ago_in_words service.updated_at = time_ago_in_words service.updated_at
ago ago
...@@ -75,6 +75,16 @@ ...@@ -75,6 +75,16 @@
stage: deploy stage: deploy
script: script:
- "curl -X POST -F token=TOKEN -F ref=REF_NAME #{builds_trigger_url(@project.id)}" - "curl -X POST -F token=TOKEN -F ref=REF_NAME #{builds_trigger_url(@project.id)}"
%h5.prepend-top-default
Use webhook
%p.light
Add the following webhook to another project for Push and Tag push events.
The project will be rebuilt at the corresponding event.
%pre
:plain
#{builds_trigger_url(@project.id, ref: 'REF_NAME')}?token=TOKEN
%h5.prepend-top-default %h5.prepend-top-default
Pass build variables Pass build variables
...@@ -83,10 +93,18 @@ ...@@ -83,10 +93,18 @@
%code variables[VARIABLE]=VALUE %code variables[VARIABLE]=VALUE
to an API request. Variable values can be used to distinguish between triggered builds and normal builds. to an API request. Variable values can be used to distinguish between triggered builds and normal builds.
%pre.append-bottom-0 With cURL:
%pre
:plain :plain
curl -X POST \ curl -X POST \
-F token=TOKEN \ -F token=TOKEN \
-F "ref=REF_NAME" \ -F "ref=REF_NAME" \
-F "variables[RUN_NIGHTLY_BUILD]=true" \ -F "variables[RUN_NIGHTLY_BUILD]=true" \
#{builds_trigger_url(@project.id)} #{builds_trigger_url(@project.id)}
%p.light
With webhook:
%pre.append-bottom-0
:plain
#{builds_trigger_url(@project.id, ref: 'REF_NAME')}?token=TOKEN&variables[RUN_NIGHTLY_BUILD]=true
- blob = parse_search_result(blob) - file_name, blob = blob
.blob-result .blob-result
.file-holder .file-holder
.file-title .file-title
- blob_link = namespace_project_blob_path(@project.namespace, @project, tree_join(blob.ref, blob.filename)) - ref = @search_results.repository_ref
- blob_link = namespace_project_blob_path(@project.namespace, @project, tree_join(ref, file_name))
= link_to blob_link do = link_to blob_link do
%i.fa.fa-file %i.fa.fa-file
%strong %strong
= blob.filename = file_name
- if blob
.file-content.code.term .file-content.code.term
= render 'shared/file_highlight', blob: blob, first_line_number: blob.startline, blob_link: blob_link = render 'shared/file_highlight', blob: blob, first_line_number: blob.startline, blob_link: blob_link
- form = local_assigns.fetch(:f)
- project = @target_project || @project - project = @target_project || @project
= form_errors(issuable) = form_errors(issuable)
...@@ -10,44 +11,17 @@ ...@@ -10,44 +11,17 @@
and make sure your changes will not unintentionally remove theirs and make sure your changes will not unintentionally remove theirs
.form-group .form-group
= f.label :title, class: 'control-label' = form.label :title, class: 'control-label'
= render 'shared/issuable/form/template_selector', issuable: issuable = render 'shared/issuable/form/template_selector', issuable: issuable
= render 'shared/issuable/form/title', issuable: issuable, form: form
%div{ class: issuable_templates(issuable).any? ? 'col-sm-7 col-lg-8' : 'col-sm-10' }
= f.text_field :title, maxlength: 255, autofocus: true, autocomplete: 'off',
class: 'form-control pad', required: true
- if issuable.is_a?(MergeRequest)
%p.help-block
.js-wip-explanation
%a.js-toggle-wip{href: "", tabindex: -1}
Remove the
%code WIP:
prefix from the title
to allow this
%strong Work In Progress
merge request to be merged when it's ready.
.js-no-wip-explanation
%a.js-toggle-wip{href: "", tabindex: -1}
Start the title with
%code WIP:
to prevent a
%strong Work In Progress
merge request from being merged before it's ready.
- if can_add_template?(issuable)
%p.help-block
Add
= link_to "description templates", help_page_path('user/project/description_templates'), tabindex: -1
to help your contributors communicate effectively!
.form-group.detail-page-description .form-group.detail-page-description
= f.label :description, 'Description', class: 'control-label' = form.label :description, 'Description', class: 'control-label'
.col-sm-10 .col-sm-10
= render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do
= render 'projects/zen', f: f, attr: :description, = render 'projects/zen', f: form, attr: :description,
classes: 'note-textarea', classes: 'note-textarea',
placeholder: "Write a comment or drag your files here...", placeholder: "Write a comment or drag your files here...",
supports_slash_commands: !issuable.persisted? supports_slash_commands: !issuable.persisted?
...@@ -59,8 +33,8 @@ ...@@ -59,8 +33,8 @@
.form-group .form-group
.col-sm-offset-2.col-sm-10 .col-sm-offset-2.col-sm-10
.checkbox .checkbox
= f.label :confidential do = form.label :confidential do
= f.check_box :confidential = form.check_box :confidential
This issue is confidential and should only be visible to team members with at least Reporter access. This issue is confidential and should only be visible to team members with at least Reporter access.
- if can?(current_user, :"admin_#{issuable.to_ability_name}", issuable.project) - if can?(current_user, :"admin_#{issuable.to_ability_name}", issuable.project)
...@@ -69,32 +43,32 @@ ...@@ -69,32 +43,32 @@
.row .row
%div{ class: (has_due_date ? "col-lg-6" : "col-sm-12") } %div{ class: (has_due_date ? "col-lg-6" : "col-sm-12") }
.form-group.issue-assignee .form-group.issue-assignee
= f.label :assignee_id, "Assignee", class: "control-label #{"col-lg-4" if has_due_date}" = form.label :assignee_id, "Assignee", class: "control-label #{"col-lg-4" if has_due_date}"
.col-sm-10{ class: ("col-lg-8" if has_due_date) } .col-sm-10{ class: ("col-lg-8" if has_due_date) }
.issuable-form-select-holder .issuable-form-select-holder
- if issuable.assignee_id - if issuable.assignee_id
= f.hidden_field :assignee_id = form.hidden_field :assignee_id
= dropdown_tag(user_dropdown_label(issuable.assignee_id, "Assignee"), options: { toggle_class: "js-dropdown-keep-input js-user-search js-issuable-form-dropdown js-assignee-search", title: "Select assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee js-filter-submit", = dropdown_tag(user_dropdown_label(issuable.assignee_id, "Assignee"), options: { toggle_class: "js-dropdown-keep-input js-user-search js-issuable-form-dropdown js-assignee-search", title: "Select assignee", filter: true, dropdown_class: "dropdown-menu-user dropdown-menu-selectable dropdown-menu-assignee js-filter-submit",
placeholder: "Search assignee", data: { first_user: current_user.try(:username), null_user: true, current_user: true, project_id: project.try(:id), selected: issuable.assignee_id, field_name: "#{issuable.class.model_name.param_key}[assignee_id]", default_label: "Assignee"} }) placeholder: "Search assignee", data: { first_user: current_user.try(:username), null_user: true, current_user: true, project_id: project.try(:id), selected: issuable.assignee_id, field_name: "#{issuable.class.model_name.param_key}[assignee_id]", default_label: "Assignee"} })
.form-group.issue-milestone .form-group.issue-milestone
= f.label :milestone_id, "Milestone", class: "control-label #{"col-lg-4" if has_due_date}" = form.label :milestone_id, "Milestone", class: "control-label #{"col-lg-4" if has_due_date}"
.col-sm-10{ class: ("col-lg-8" if has_due_date) } .col-sm-10{ class: ("col-lg-8" if has_due_date) }
.issuable-form-select-holder .issuable-form-select-holder
= render "shared/issuable/milestone_dropdown", selected: issuable.milestone, name: "#{issuable.class.model_name.param_key}[milestone_id]", show_any: false, show_upcoming: false, extra_class: "js-issuable-form-dropdown js-dropdown-keep-input", dropdown_title: "Select milestone" = render "shared/issuable/milestone_dropdown", selected: issuable.milestone, name: "#{issuable.class.model_name.param_key}[milestone_id]", show_any: false, show_upcoming: false, extra_class: "js-issuable-form-dropdown js-dropdown-keep-input", dropdown_title: "Select milestone"
.form-group .form-group
- has_labels = @labels && @labels.any? - has_labels = @labels && @labels.any?
= f.label :label_ids, "Labels", class: "control-label #{"col-lg-4" if has_due_date}" = form.label :label_ids, "Labels", class: "control-label #{"col-lg-4" if has_due_date}"
= f.hidden_field :label_ids, multiple: true, value: '' = form.hidden_field :label_ids, multiple: true, value: ''
.col-sm-10{ class: "#{"col-lg-8" if has_due_date} #{'issuable-form-padding-top' if !has_labels}" } .col-sm-10{ class: "#{"col-lg-8" if has_due_date} #{'issuable-form-padding-top' if !has_labels}" }
.issuable-form-select-holder .issuable-form-select-holder
= render "shared/issuable/label_dropdown", classes: ["js-issuable-form-dropdown"], selected: issuable.labels, data_options: { field_name: "#{issuable.class.model_name.param_key}[label_ids][]", show_any: false}, dropdown_title: "Select label" = render "shared/issuable/label_dropdown", classes: ["js-issuable-form-dropdown"], selected: issuable.labels, data_options: { field_name: "#{issuable.class.model_name.param_key}[label_ids][]", show_any: false}, dropdown_title: "Select label"
- if has_due_date - if has_due_date
.col-lg-6 .col-lg-6
.form-group .form-group
= f.label :due_date, "Due date", class: "control-label" = form.label :due_date, "Due date", class: "control-label"
.col-sm-10 .col-sm-10
.issuable-form-select-holder .issuable-form-select-holder
= f.text_field :due_date, id: "issuable-due-date", class: "datepicker form-control", placeholder: "Select due date" = form.text_field :due_date, id: "issuable-due-date", class: "datepicker form-control", placeholder: "Select due date"
- if issuable.can_move?(current_user) - if issuable.can_move?(current_user)
%hr %hr
...@@ -112,15 +86,15 @@ ...@@ -112,15 +86,15 @@
%hr %hr
- if @merge_request.new_record? - if @merge_request.new_record?
.form-group .form-group
= f.label :source_branch, class: 'control-label' = form.label :source_branch, class: 'control-label'
.col-sm-10 .col-sm-10
.issuable-form-select-holder .issuable-form-select-holder
= f.select(:source_branch, [@merge_request.source_branch], { }, { class: 'source_branch select2 span2', disabled: true }) = form.select(:source_branch, [@merge_request.source_branch], { }, { class: 'source_branch select2 span2', disabled: true })
.form-group .form-group
= f.label :target_branch, class: 'control-label' = form.label :target_branch, class: 'control-label'
.col-sm-10 .col-sm-10
.issuable-form-select-holder .issuable-form-select-holder
= f.select(:target_branch, @merge_request.target_branches, { include_blank: true }, { class: 'target_branch select2 span2', disabled: @merge_request.new_record?, data: {placeholder: "Select branch"} }) = form.select(:target_branch, @merge_request.target_branches, { include_blank: true }, { class: 'target_branch select2 span2', disabled: @merge_request.new_record?, data: {placeholder: "Select branch"} })
- if @merge_request.new_record? - if @merge_request.new_record?
&nbsp; &nbsp;
= link_to 'Change branches', mr_change_branches_path(@merge_request) = link_to 'Change branches', mr_change_branches_path(@merge_request)
...@@ -136,9 +110,9 @@ ...@@ -136,9 +110,9 @@
- is_footer = !(issuable.is_a?(MergeRequest) && issuable.new_record?) - is_footer = !(issuable.is_a?(MergeRequest) && issuable.new_record?)
.row-content-block{class: (is_footer ? "footer-block" : "middle-block")} .row-content-block{class: (is_footer ? "footer-block" : "middle-block")}
- if issuable.new_record? - if issuable.new_record?
= f.submit "Submit #{issuable.class.model_name.human.downcase}", class: 'btn btn-create' = form.submit "Submit #{issuable.class.model_name.human.downcase}", class: 'btn btn-create'
- else - else
= f.submit 'Save changes', class: 'btn btn-save' = form.submit 'Save changes', class: 'btn btn-save'
- if !issuable.persisted? && !issuable.project.empty_repo? && (guide_url = contribution_guide_path(issuable.project)) - if !issuable.persisted? && !issuable.project.empty_repo? && (guide_url = contribution_guide_path(issuable.project))
.inline.prepend-left-10 .inline.prepend-left-10
...@@ -155,4 +129,4 @@ ...@@ -155,4 +129,4 @@
method: :delete, class: 'btn btn-danger btn-grouped' method: :delete, class: 'btn btn-danger btn-grouped'
= link_to 'Cancel', polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), class: 'btn btn-grouped btn-cancel' = link_to 'Cancel', polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), class: 'btn btn-grouped btn-cancel'
= f.hidden_field :lock_version = form.hidden_field :lock_version
- issuable = local_assigns.fetch(:issuable)
- form = local_assigns.fetch(:form)
- no_issuable_templates = issuable_templates(issuable).empty?
- div_class = no_issuable_templates ? 'col-sm-10' : 'col-sm-7 col-lg-8'
%div{ class: div_class }
= form.text_field :title, required: true, maxlength: 255, autofocus: true,
autocomplete: 'off', class: 'form-control pad'
- if issuable.respond_to?(:work_in_progress?)
%p.help-block
.js-wip-explanation
%a.js-toggle-wip{ href: '', tabindex: -1 }
Remove the
%code WIP:
prefix from the title
to allow this
%strong Work In Progress
merge request to be merged when it's ready.
.js-no-wip-explanation
%a.js-toggle-wip{ href: '', tabindex: -1 }
Start the title with
%code WIP:
to prevent a
%strong Work In Progress
merge request from being merged before it's ready.
- if no_issuable_templates && can?(current_user, :push_code, issuable.project)
%p.help-block
Add
= link_to 'description templates', help_page_path('user/project/description_templates'), tabindex: -1
to help your contributors communicate effectively!
class DeleteMergedBranchesWorker
include Sidekiq::Worker
include DedicatedSidekiqQueue
def perform(project_id, user_id)
begin
project = Project.find(project_id)
rescue ActiveRecord::RecordNotFound
return
end
user = User.find(user_id)
begin
DeleteMergedBranchesService.new(project, user).execute
rescue Gitlab::Access::AccessDeniedError
return
end
end
end
---
title: Add setting to only allow merge requests to be merged when all discussions are resolved
merge_request: 7125
author: Rodolfo Arruda
---
title: Add button to delete all merged branches
merge_request: 6449
author: Toon Claes
---
title: Use the Gitlab Workhorse HTTP header in the admin dashboard
merge_request:
author: Chris Wright
---
title: Disable "Request Access" functionality by default for new projects and groups
merge_request: 7425
author:
---
title: 'Fix: Todos Filter Shows All Users'
merge_request:
author:
---
title: Limit autocomplete to currently selected items for unlabel slash command
merge_request: 22680
author: Akram Fares
---
title: Show avatars in mention dropdown
merge_request: 6865
author:
---
title: Issues atom feed url reflect filters on dashboard
merge_request: 7114
author: Lucas Deschamps
---
title: Rewrite git blame spinach feature tests to rspec feature tests
merge_request: 7197
author: Lisanne Fellinger
---
title: Search for a filename in a project
merge_request:
author:
---
title: Make it possible to trigger builds from webhooks
merge_request: 7022
author: Dmitry Poray
---
title: Add query param to filter users by external & blocked type
merge_request: 7109
author: Yatish Mehta
---
title: Allow commit note to be visible if repo is visible
merge_request:
author:
---
title: Only skip group when it's actually a group in the "Share with group" select
merge_request: 7262
author:
---
title: 'Fix: Guest sees some repository details and gets 404'
merge_request:
author:
---
title: Introduce round-robin project creation to spread load over multiple shards
merge_request: 7266
author:
---
title: Ensure merge request's "remove branch" accessors return booleans
merge_request: 7267
author:
---
title: Fix broken commits search
merge_request:
author:
---
title: Adds es6-promise Polyfill
merge_request: 7482
author:
---
title: Fix POST /internal/allowed to cope with gitlab-shell v4.0.0 project paths
merge_request: 7480
author:
---
title: Defer saving project services to the database if there are no user changes
merge_request: 6958
author:
---
title: Expose label IDs in API
merge_request: 7275
author: Rares Sfirlogea
---
title: Add an index for project_id in project_import_data to improve performance
merge_request:
author:
---
title: "API: Ability to retrieve version information"
merge_request: 7286
author: Robert Schilling
---
title: Return 400 when creating a system hook fails
merge_request: 7350
author: Robert Schilling
---
title: Fix broken link to observatory cli on Frontend Dev Guide
merge_request:
author: Sam Rose
---
title: Faster search inside Project
merge_request:
author:
---
title: Fix 404 on network page when entering non-existent git revision
merge_request: 7172
author: Hiroyuki Sato
---
title: Fix invalid filename validation on eslint
merge_request: 7281
author:
---
title: Do not create a MergeRequestDiff record when source branch is deleted
merge_request: 7481
author:
---
title: fix shibboleth misconfigurations resulting in authentication bypass
merge_request: 7428
author:
---
title: Clicking "force remove source branch" label now toggles the checkbox again
merge_request:
author:
---
title: Fix labels API by adding missing current_user parameter
merge_request: 7458
author: Francesco Coda Zabetta
---
title: Navigation bar issuables counters reflects dashboard issuables counters
merge_request: 7368
author: Lucas Deschamps
---
title: Omniauth auto link LDAP user falls back to find by DN when user cannot be found
by UID
merge_request: 7002
author:
---
title: Finer-grained Git gargage collection
merge_request: 6588
author:
---
title: Fixed issue boards counter border when unauthorized
merge_request:
author:
---
title: Allow to test JIRA service settings without having a repository
merge_request:
author:
---
title: Introduce better credential and error checking to `rake gitlab:ldap:check`
merge_request: 6601
author:
---
title: API: allow recursive tree request
merge_request: 6088
author: Rebeca Méndez
---
title: Add CI notifications. Who triggered a pipeline would receive an email after
the pipeline is succeeded or failed. Users could also update notification settings
accordingly
merge_request: 6342
author:
---
title: Process commits using a dedicated Sidekiq worker
merge_request: 6802
author:
---
title: Remove an extra leading space from diff paste data
merge_request: 7133
author: Hiroyuki Sato
---
title: Limit labels returned for a specific project as an administrator
merge_request: 7496
author:
---
title: Use setter for key instead AR callback
merge_request: 7488
author: Semyon Pupkov
---
title: Bump omniauth-gitlab to 1.0.2 to fix incompatibility with omniauth-oauth2
merge_request:
author:
---
title: Fix showing pipeline status for a given commit from correct branch
merge_request: 7034
author:
---
title: Set default Sidekiq retries to 3
merge_request: 7294
author:
---
title: Fix Error 500 when creating a merge request that contains an image that was deleted and added
merge_request: 7457
author:
---
title: Replace jQuery.timeago with timeago.js
merge_request: 6274
author: ClemMakesApps
---
title: Use separate email-token for incoming email and revert back the inactive feature
merge_request: 5914
author:
---
title: Fixed multiple requests sent when opening dropdowns
merge_request:
author:
...@@ -241,6 +241,10 @@ Devise.setup do |config| ...@@ -241,6 +241,10 @@ Devise.setup do |config|
end end
end end
if provider['name'] == 'shibboleth'
provider['args'][:fail_with_empty_uid] = true
end
# A Hash from the configuration will be passed as is. # A Hash from the configuration will be passed as is.
provider_arguments << provider['args'].symbolize_keys provider_arguments << provider['args'].symbolize_keys
end end
......
...@@ -125,6 +125,7 @@ resources :namespaces, path: '/', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only: ...@@ -125,6 +125,7 @@ resources :namespaces, path: '/', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only:
end end
resources :branches, only: [:index, :new, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex } resources :branches, only: [:index, :new, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex }
delete :merged_branches, controller: 'branches', action: :destroy_all_merged
resources :tags, only: [:index, :show, :new, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex } do resources :tags, only: [:index, :show, :new, :create, :destroy], constraints: { id: Gitlab::Regex.git_reference_regex } do
resource :release, only: [:edit, :update] resource :release, only: [:edit, :update]
end end
......
...@@ -34,6 +34,7 @@ ...@@ -34,6 +34,7 @@
- [project_service, 1] - [project_service, 1]
- [clear_database_cache, 1] - [clear_database_cache, 1]
- [delete_user, 1] - [delete_user, 1]
- [delete_merged_branches, 1]
- [expire_build_instance_artifacts, 1] - [expire_build_instance_artifacts, 1]
- [group_destroy, 1] - [group_destroy, 1]
- [irker, 1] - [irker, 1]
......
class DefaultRequestAccessGroups < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
change_column_default :namespaces, :request_access_enabled, false
end
def down
change_column_default :namespaces, :request_access_enabled, true
end
end
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment