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
......@@ -4,37 +4,6 @@ entry.
## 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)
- Fix Milestone dropdown not stay selected for `Upcoming` and `No Milestone` option !7117
- Diff collapse won't shift when collapsing.
......@@ -104,6 +73,7 @@ entry.
- 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)
- Updated commit SHA styling on the branches page.
- Fix "Without projects" filter. !6611 (Ben Bodenmiller)
- Fix 404 when visit /projects page
## 8.13.5 (2016-11-08)
......
......@@ -9,8 +9,6 @@
- [Helping others](#helping-others)
- [I want to contribute!](#i-want-to-contribute)
- [Implement design & UI elements](#implement-design-ui-elements)
- [Design reference](#design-reference)
- [UI development kit](#ui-development-kit)
- [Issue tracker](#issue-tracker)
- [Feature proposals](#feature-proposals)
- [Issue tracker guidelines](#issue-tracker-guidelines)
......@@ -90,7 +88,7 @@ This was inspired by [an article by Kent C. Dodds][medium-up-for-grabs].
## Implement design & UI elements
Please see the [UI Guide for building GitLab].
Please see the [UX Guide for GitLab].
## Issue tracker
......@@ -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"
[scss-styleguide]: doc/development/scss_styleguide.md "SCSS 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
......@@ -53,6 +53,7 @@
/*= require_directory ./u2f */
/*= require_directory . */
/*= require fuzzaldrin-plus */
/*= require es6-promise.auto */
(function () {
document.addEventListener('page:fetch', gl.utils.cleanupBeforeFetch);
......
......@@ -16,7 +16,7 @@
},
// Team Members
Members: {
template: '<li>${username} <small>${title}</small></li>'
template: '<li>${avatarTag} ${username} <small>${title}</small></li>'
},
Labels: {
template: '<li><span class="dropdown-label-box" style="background: ${color}"></span> ${title}</li>'
......@@ -51,6 +51,11 @@
if (!GitLab.GfmAutoComplete.dataLoaded) {
return this.at;
} 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;
}
}
......@@ -118,7 +123,7 @@
beforeInsert: this.DefaultOptions.beforeInsert,
beforeSave: function(members) {
return $.map(members, function(m) {
var title;
let title = '';
if (m.username == null) {
return m;
}
......@@ -126,8 +131,14 @@
if (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 {
username: m.username,
avatarTag: autoCompleteAvatar.length === 1 ? txtAvatar : imgAvatar,
title: gl.utils.sanitize(title),
search: gl.utils.sanitize(m.username + " " + m.name)
};
......@@ -352,3 +363,4 @@
};
}).call(this);
......@@ -249,7 +249,7 @@
_this.fullData = data;
_this.parseData(_this.fullData);
_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');
}
};
......
......@@ -34,6 +34,7 @@
&.avatar-inline {
float: none;
display: inline-block;
margin-left: 4px;
margin-bottom: 2px;
......@@ -41,6 +42,12 @@
&.s24 { margin-right: 4px; }
}
&.center {
font-size: 14px;
line-height: 1.8em;
text-align: center;
}
&.avatar-tile {
border-radius: 0;
border: none;
......
......@@ -141,6 +141,10 @@
&.btn-save {
@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 {
......
......@@ -63,7 +63,11 @@ header {
&:focus,
&:active {
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 {
......@@ -194,7 +198,7 @@ header {
cursor: pointer;
&:hover {
color: darken($color: $gl-text-color, $amount: 50%);
color: darken($color: $gl-text-color, $amount: 30%);
}
}
......
......@@ -148,7 +148,19 @@
}
}
.atwho-view small.description {
float: right;
padding: 3px 5px;
}
.atwho-view {
small.description {
float: right;
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 @@
border-bottom: none;
}
}
.blob-result {
margin: 5px 0;
}
}
.search {
......@@ -157,7 +161,6 @@
width: 68%;
}
}
}
.search-holder {
......@@ -234,5 +237,4 @@
&:focus {
color: $gl-link-color;
}
}
......@@ -4,7 +4,7 @@ class Projects::BranchesController < Projects::ApplicationController
# Authorize
before_action :require_non_empty_project
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
@sort = params[:sort].presence || sort_value_name
......@@ -62,6 +62,13 @@ class Projects::BranchesController < Projects::ApplicationController
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
def ref
......
......@@ -31,10 +31,6 @@ class Projects::LfsApiController < Projects::GitHttpClientController
private
def objects
@objects ||= (params[:objects] || []).to_a
end
def existing_oids
@existing_oids ||= begin
storage_project.lfs_objects.where(oid: objects.map { |o| o['oid'].to_s }).pluck(:oid)
......
......@@ -10,8 +10,7 @@ class Projects::ServicesController < Projects::ApplicationController
layout "project_settings"
def index
@project.build_missing_services
@services = @project.services.visible.reload
@services = @project.find_or_initialize_services
end
def edit
......@@ -46,6 +45,6 @@ class Projects::ServicesController < Projects::ApplicationController
private
def service
@service ||= @project.services.find { |service| service.to_param == params[:id] }
@service ||= @project.find_or_initialize_service(params[:id])
end
end
......@@ -144,13 +144,15 @@ class ProjectsController < Projects::ApplicationController
autocomplete = ::Projects::AutocompleteService.new(@project, current_user)
participants = ::Projects::ParticipantsService.new(@project, current_user).execute(noteable)
unlabels = autocomplete.unlabels(noteable)
@suggestions = {
emojis: Gitlab::AwardEmoji.urls,
issues: autocomplete.issues,
milestones: autocomplete.milestones,
mergerequests: autocomplete.merge_requests,
labels: autocomplete.labels,
labels: autocomplete.labels - unlabels,
unlabels: unlabels,
members: participants,
commands: autocomplete.commands(noteable, params[:type])
}
......
......@@ -6,7 +6,7 @@ class LabelsFinder < UnionFinder
def execute(skip_authorization: false)
@skip_authorization = skip_authorization
items = find_union(label_ids, Label)
items = find_union(label_ids, Label) || Label.none
items = with_title(items)
sort(items)
end
......@@ -18,9 +18,11 @@ class LabelsFinder < UnionFinder
def label_ids
label_ids = []
if project
label_ids << project.group.labels if project.group.present?
label_ids << project.labels
if project?
if project
label_ids << project.group.labels if project.group.present?
label_ids << project.labels
end
else
label_ids << Label.where(group_id: projects.group_ids)
label_ids << Label.where(project_id: projects.select(:id))
......@@ -40,16 +42,16 @@ class LabelsFinder < UnionFinder
items.where(title: title)
end
def group_id
params[:group_id].presence
def group?
params[:group_id].present?
end
def project_id
params[:project_id].presence
def project?
params[:project_id].present?
end
def projects_ids
params[:project_ids]
def projects?
params[:project_ids].present?
end
def title
......@@ -59,8 +61,9 @@ class LabelsFinder < UnionFinder
def project
return @project if defined?(@project)
if project_id
@project = find_project
if project?
@project = Project.find(params[:project_id])
@project = nil unless authorized_to_read_labels?(@project)
else
@project = nil
end
......@@ -68,26 +71,20 @@ class LabelsFinder < UnionFinder
@project
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
return @projects if defined?(@projects)
@projects = skip_authorization ? Project.all : available_projects
@projects = @projects.in_namespace(group_id) if group_id
@projects = @projects.where(id: projects_ids) if projects_ids
@projects = skip_authorization ? Project.all : ProjectsFinder.new.execute(current_user)
@projects = @projects.in_namespace(params[:group_id]) if group?
@projects = @projects.where(id: params[:project_ids]) if projects?
@projects = @projects.reorder(nil)
@projects
end
def available_projects
@available_projects ||= ProjectsFinder.new.execute(current_user)
def authorized_to_read_labels?(project)
return true if skip_authorization
Ability.allowed?(current_user, :read_label, project)
end
end
......@@ -86,7 +86,7 @@ module EventsHelper
elsif event.merge_request?
namespace_project_merge_request_url(event.project.namespace,
event.project, event.merge_request)
elsif event.note? && event.commit_note?
elsif event.commit_note?
namespace_project_commit_url(event.project.namespace, event.project,
event.note_target)
elsif event.note?
......@@ -127,7 +127,7 @@ module EventsHelper
end
def event_note_target_path(event)
if event.note? && event.commit_note?
if event.commit_note?
namespace_project_commit_path(event.project.namespace,
event.project,
event.note_target,
......
......@@ -30,11 +30,6 @@ module IssuablesHelper
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)
title = selected_template(issuable) || "Choose a template"
options = {
......@@ -141,8 +136,19 @@ module IssuablesHelper
html.html_safe
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
def assigned_issuables_count(assignee, issuable_type, state)
assignee.public_send("assigned_#{issuable_type}").public_send(state).count
end
def sidebar_gutter_collapsed?
cookies[:collapsed_gutter] == 'true'
end
......
......@@ -30,6 +30,10 @@ module LfsHelper
ci? || lfs_deploy_token? || user_can_download_code? || build_can_download_code?
end
def objects
@objects ||= (params[:objects] || []).to_a
end
def user_can_download_code?
has_authentication_ability?(:download_code) && can?(user, :download_code, project)
end
......
......@@ -31,34 +31,7 @@ module SearchHelper
end
def parse_search_result(result)
ref = nil
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
)
Gitlab::ProjectSearchResults.parse_search_result(result)
end
private
......
module TriggersHelper
def builds_trigger_url(project_id)
"#{Settings.gitlab.url}/api/v3/projects/#{project_id}/trigger/builds"
def builds_trigger_url(project_id, ref: nil)
if ref.nil?
"#{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
......@@ -62,7 +62,7 @@ class Event < ActiveRecord::Base
end
def visible_to_user?(user = nil)
if push?
if push? || commit_note?
Ability.allowed?(user, :download_code, project)
elsif membership_changed?
true
......@@ -283,7 +283,7 @@ class Event < ActiveRecord::Base
end
def commit_note?
target.for_commit?
note? && target && target.for_commit?
end
def issue_note?
......@@ -295,7 +295,7 @@ class Event < ActiveRecord::Base
end
def project_snippet_note?
target.for_snippet?
note? && target && target.for_snippet?
end
def note_target
......
......@@ -6,7 +6,7 @@ class Key < ActiveRecord::Base
belongs_to :user
before_validation :strip_white_space, :generate_fingerprint
before_validation :generate_fingerprint
validates :title, presence: true, length: { within: 0..255 }
validates :key, presence: true, length: { within: 0..5000 }, format: { with: /\A(ssh|ecdsa)-.*\Z/ }
......@@ -21,8 +21,9 @@ class Key < ActiveRecord::Base
after_destroy :remove_from_shell
after_destroy :post_destroy_hook
def strip_white_space
self.key = key.strip unless key.blank?
def key=(value)
value.strip! unless value.blank?
write_attribute(:key, value)
end
def publishable_key
......
......@@ -35,6 +35,7 @@ class Project < ActiveRecord::Base
default_value_for :builds_enabled, gitlab_config_features.builds
default_value_for :wiki_enabled, gitlab_config_features.wiki
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 :create_project_feature, unless: :project_feature
......@@ -76,7 +77,6 @@ class Project < ActiveRecord::Base
has_many :boards, before_add: :validate_board_limit, dependent: :destroy
# Project services
has_many :services
has_one :campfire_service, dependent: :destroy
has_one :drone_ci_service, dependent: :destroy
has_one :emails_on_push_service, dependent: :destroy
......@@ -748,27 +748,32 @@ class Project < ActiveRecord::Base
update_column(:has_external_wiki, services.external_wikis.any?)
end
def build_missing_services
def find_or_initialize_services
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)
# If service is available but missing in db
if service.nil?
if service
service
else
# We should check if template for the service exists
template = find_service(services_templates, service_name)
if template.nil?
# If no template, we should create an instance. Ex `create_gitlab_ci_service`
public_send("create_#{service_name}_service")
# If no template, we should create an instance. Ex `build_gitlab_ci_service`
public_send("build_#{service_name}_service")
else
Service.create_from_template(self.id, template)
Service.build_from_template(id, template)
end
end
end
end
def find_or_initialize_service(name)
find_or_initialize_services.find { |service| service.to_param == name }
end
def create_labels
Label.templates.each do |label|
params = label.attributes.except('id', 'template', 'created_at', 'updated_at')
......@@ -878,7 +883,7 @@ class Project < ActiveRecord::Base
end
def empty_repo?
!repository.exists? || !repository.has_visible_content?
repository.empty_repo?
end
def repo
......@@ -1334,10 +1339,6 @@ class Project < ActiveRecord::Base
end
end
def only_allow_merge_if_all_discussions_are_resolved
super || false
end
private
def pushes_since_gc_redis_key
......
......@@ -127,7 +127,7 @@ class ProjectWiki
end
def search_files(query)
repository.search_files(query, default_branch)
repository.search_files_by_content(query, default_branch)
end
def repository
......
......@@ -15,16 +15,6 @@ class Repository
Gitlab.config.repositories.storages
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)
@path_with_namespace = path_with_namespace
@project = project
......@@ -631,7 +621,7 @@ class Repository
@head_tree ||= Tree.new(self, head_commit.sha, nil)
end
def tree(sha = :head, path = nil)
def tree(sha = :head, path = nil, recursive: false)
if sha == :head
if path.nil?
return head_tree
......@@ -640,7 +630,7 @@ class Repository
end
end
Tree.new(self, sha, path)
Tree.new(self, sha, path, recursive: recursive)
end
def blob_at_branch(branch_name, path)
......@@ -1063,16 +1053,25 @@ class Repository
merge_base(ancestor_id, descendant_id) == ancestor_id
end
def search_files(query, ref)
unless exists? && has_visible_content? && query.present?
return []
end
def empty_repo?
!exists? || !has_visible_content?
end
def search_files_by_content(query, ref)
return [] if empty_repo? || query.blank?
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})
Gitlab::Popen.popen(args, path_to_repo).first.scrub.split(/^--$/)
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)
args = %W(#{Gitlab.config.git.bin_path} fetch --no-tags -f #{source_path} #{source_ref}:#{target_ref})
Gitlab::Popen.popen(args, path_to_repo)
......
......@@ -222,11 +222,11 @@ class Service < ActiveRecord::Base
]
end
def self.create_from_template(project_id, template)
def self.build_from_template(project_id, template)
service = template.dup
service.template = false
service.project_id = project_id
service if service.save
service
end
private
......
......@@ -3,15 +3,16 @@ class Tree
attr_accessor :repository, :sha, :path, :entries
def initialize(repository, sha, path = '/')
def initialize(repository, sha, path = '/', recursive: false)
path = '/' if path.blank?
@repository = repository
@sha = sha
@path = path
@recursive = recursive
git_repo = @repository.raw_repository
@entries = Gitlab::Git::Tree.where(git_repo, @sha, @path)
@entries = get_entries(git_repo, @sha, @path, recursive: @recursive)
end
def readme
......@@ -58,4 +59,21 @@ class Tree
def sorted_entries
trees + blobs + submodules
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
......@@ -173,7 +173,7 @@ class User < ActiveRecord::Base
scope :external, -> { where(external: true) }
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 :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)) }
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
merge_requests = filter_merge_requests(merge_requests)
merge_requests.each do |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
reload_diff(merge_request) unless branch_removed?
merge_request.mark_as_unchecked
end
end
......@@ -173,5 +165,16 @@ module MergeRequests
def branch_removed?
Gitlab::Git.blank_ref?(@newrev)
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
......@@ -13,7 +13,14 @@ module Projects
end
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
def commands(noteable, type)
......
......@@ -95,7 +95,7 @@ module Projects
unless @project.gitlab_project_import?
@project.create_wiki unless skip_wiki?
@project.build_missing_services
create_services_from_active_templates(@project)
@project.create_labels
end
......@@ -135,5 +135,12 @@ module Projects
@project
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
module Projects
class ParticipantsService < BaseService
attr_reader :noteable
def execute(noteable)
@noteable = noteable
......@@ -15,7 +15,8 @@ module Projects
[{
name: noteable.author.name,
username: noteable.author.username
username: noteable.author.username,
avatar_url: noteable.author.avatar_url
}]
end
......@@ -28,14 +29,14 @@ module Projects
def sorted(users)
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
def groups
current_user.authorized_groups.sort_by(&:path).map do |group|
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
......
......@@ -26,12 +26,12 @@
= link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues' do
%span
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
= link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do
%span
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
= link_to dashboard_snippets_path, title: 'Snippets' do
%span
......
......@@ -7,7 +7,7 @@
data: { container: "body", placement: "bottom" } }
{{ list.title }}
.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 }}
- if can?(current_user, :admin_issue, @project)
%button.btn.btn-small.btn-default.pull-right.has-tooltip{ type: "button",
......
......@@ -26,6 +26,8 @@
= sort_title_oldest_updated
- 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
New branch
......
......@@ -25,7 +25,7 @@
- elsif diff_file.renamed_file
.nothing-here-block File moved
- 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
- else
.nothing-here-block No preview for this file type
......@@ -28,5 +28,6 @@
%td.hidden-xs
= service.description
%td.light
= time_ago_in_words service.updated_at
ago
- if service.updated_at.present?
= time_ago_in_words service.updated_at
ago
......@@ -75,6 +75,16 @@
stage: deploy
script:
- "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
Pass build variables
......@@ -83,10 +93,18 @@
%code variables[VARIABLE]=VALUE
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
curl -X POST \
-F token=TOKEN \
-F "ref=REF_NAME" \
-F "variables[RUN_NIGHTLY_BUILD]=true" \
#{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
.file-holder
.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
%i.fa.fa-file
%strong
= blob.filename
.file-content.code.term
= render 'shared/file_highlight', blob: blob, first_line_number: blob.startline, blob_link: blob_link
= file_name
- if blob
.file-content.code.term
= render 'shared/file_highlight', blob: blob, first_line_number: blob.startline, blob_link: blob_link
- form = local_assigns.fetch(:f)
- project = @target_project || @project
= form_errors(issuable)
......@@ -10,44 +11,17 @@
and make sure your changes will not unintentionally remove theirs
.form-group
= f.label :title, class: 'control-label'
= form.label :title, class: 'control-label'
= render 'shared/issuable/form/template_selector', issuable: issuable
%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!
= render 'shared/issuable/form/title', issuable: issuable, form: form
.form-group.detail-page-description
= f.label :description, 'Description', class: 'control-label'
= form.label :description, 'Description', class: 'control-label'
.col-sm-10
= 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',
placeholder: "Write a comment or drag your files here...",
supports_slash_commands: !issuable.persisted?
......@@ -59,8 +33,8 @@
.form-group
.col-sm-offset-2.col-sm-10
.checkbox
= f.label :confidential do
= f.check_box :confidential
= form.label :confidential do
= form.check_box :confidential
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)
......@@ -69,32 +43,32 @@
.row
%div{ class: (has_due_date ? "col-lg-6" : "col-sm-12") }
.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) }
.issuable-form-select-holder
- 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",
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
= 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) }
.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"
.form-group
- has_labels = @labels && @labels.any?
= f.label :label_ids, "Labels", class: "control-label #{"col-lg-4" if has_due_date}"
= f.hidden_field :label_ids, multiple: true, value: ''
= form.label :label_ids, "Labels", class: "control-label #{"col-lg-4" if has_due_date}"
= 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}" }
.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"
- if has_due_date
.col-lg-6
.form-group
= f.label :due_date, "Due date", class: "control-label"
= form.label :due_date, "Due date", class: "control-label"
.col-sm-10
.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)
%hr
......@@ -112,15 +86,15 @@
%hr
- if @merge_request.new_record?
.form-group
= f.label :source_branch, class: 'control-label'
= form.label :source_branch, class: 'control-label'
.col-sm-10
.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
= f.label :target_branch, class: 'control-label'
= form.label :target_branch, class: 'control-label'
.col-sm-10
.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?
&nbsp;
= link_to 'Change branches', mr_change_branches_path(@merge_request)
......@@ -136,9 +110,9 @@
- is_footer = !(issuable.is_a?(MergeRequest) && issuable.new_record?)
.row-content-block{class: (is_footer ? "footer-block" : "middle-block")}
- 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
= 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))
.inline.prepend-left-10
......@@ -155,4 +129,4 @@
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'
= 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|
end
end
if provider['name'] == 'shibboleth'
provider['args'][:fail_with_empty_uid] = true
end
# A Hash from the configuration will be passed as is.
provider_arguments << provider['args'].symbolize_keys
end
......
......@@ -125,6 +125,7 @@ resources :namespaces, path: '/', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only:
end
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
resource :release, only: [:edit, :update]
end
......
......@@ -34,6 +34,7 @@
- [project_service, 1]
- [clear_database_cache, 1]
- [delete_user, 1]
- [delete_merged_branches, 1]
- [expire_build_instance_artifacts, 1]
- [group_destroy, 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