Commit 38405495 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'master' of dev.gitlab.org:gitlab/gitlabhq

Conflicts:
	app/assets/javascripts/dispatcher.js.coffee
	features/admin/settings.feature
	features/steps/admin/settings.rb
	features/steps/user.rb
	features/user.feature
	lib/gitlab/git_access.rb
parents 1dafe0c6 eda120dc
......@@ -355,7 +355,7 @@ Style/MultilineBlockChain:
Style/MultilineBlockLayout:
Description: 'Ensures newlines after multiline block do statements.'
Enabled: false
Enabled: true
Style/MultilineIfThen:
Description: 'Do not use then for multi-line if/unless.'
......@@ -390,7 +390,7 @@ Style/NegatedWhile:
Style/NestedTernaryOperator:
Description: 'Use one expression per branch in a ternary operator.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-ternary'
Enabled: false
Enabled: true
Style/Next:
Description: 'Use `next` to skip iteration instead of a condition at the end.'
......@@ -400,17 +400,17 @@ Style/Next:
Style/NilComparison:
Description: 'Prefer x.nil? to x == nil.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#predicate-methods'
Enabled: false
Enabled: true
Style/NonNilCheck:
Description: 'Checks for redundant nil checks.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-non-nil-checks'
Enabled: false
Enabled: true
Style/Not:
Description: 'Use ! instead of not.'
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#bang-not-not'
Enabled: false
Enabled: true
Style/NumericLiterals:
Description: >-
......@@ -424,7 +424,7 @@ Style/OneLineConditional:
Favor the ternary operator(?:) over
if/then/else/end constructs.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#ternary-operator'
Enabled: false
Enabled: true
Style/OpMethod:
Description: 'When defining binary operators, name the argument other.'
......@@ -436,7 +436,7 @@ Style/ParenthesesAroundCondition:
Don't use parentheses around the condition of an
if/unless/while.
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-parens-if'
Enabled: false
Enabled: true
Style/PercentLiteralDelimiters:
Description: 'Use `%`-literal delimiters consistently'
......@@ -480,7 +480,7 @@ Style/RedundantException:
Style/RedundantReturn:
Description: "Don't use return where it's not required."
StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-explicit-return'
Enabled: false
Enabled: true
Style/RedundantSelf:
Description: "Don't use self where it's not needed."
......
Please view this file on the master branch, on stable branches it's out of date.
v 7.10.0 (unreleased)
- Include missing events and fix save functionality in admin service template settings form (Stan Hu)
- Fix "Import projects from" button to show the correct instructions (Stan Hu)
- Fix dots in Wiki slugs causing errors (Stan Hu)
- Fix OAuth2 issue importing a new project from GitHub and GitLab (Stan Hu)
- Update poltergeist to version 1.6.0 to support PhantomJS 2.0 (Zeger-Jan van de Weg)
- Fix cross references when usernames, milestones, or project names contain underscores (Stan Hu)
- Disable reference creation for comments surrounded by code/preformatted blocks (Stan Hu)
- Reduce Rack Attack false positives causing 403 errors during HTTP authentication (Stan Hu)
- enable line wrapping per default and remove the checkbox to toggle it (Hannes Rosenögger)
- extend the commit calendar to show the actual commits made on a date (Hannes Rosenögger)
- Fix a link in the patch update guide
......@@ -12,6 +18,7 @@ v 7.10.0 (unreleased)
- Add changelog, license and contribution guide links to project sidebar.
- Improve diff UI
- Fix alignment of navbar toggle button (Cody Mize)
- Fix checkbox rendering for nested task lists
- Identical look of selectboxes in UI
- Move "Import existing repository by URL" option to button.
- Improve error message when save profile has error.
......@@ -26,6 +33,16 @@ v 7.10.0 (unreleased)
- Add changes to Deploy Keys to the Audit Logs
v 7.9.0 (unreleased)
- Replace commits calendar with faster contribution calendar that includes issues and merge requests
- Add inifinite scroll to user page activity
- Don't show commit comment button when user is not signed in.
- Don't include system notes in issue/MR comment count.
- Don't mark merge request as updated when merge status relative to target branch changes.
- Link note avatar to user.
- Make Git-over-SSH errors more descriptive.
- Send EmailsOnPush email when branch or tag is created or deleted.
v 7.9.0
- Add HipChat integration documentation (Stan Hu)
- Update documentation for object_kind field in Webhook push and tag push Webhooks (Stan Hu)
- Fix broken email images (Hannes Rosenögger)
......@@ -143,7 +160,6 @@ v 7.8.0
- Add API endpoint to fetch all changes on a MergeRequest (Jeroen van Baarsen)
- View note image attachments in new tab when clicked instead of downloading them
- Improve sorting logic in UI and API. Explicitly define what sorting method is used by default
- Allow more variations for commit messages closing issues (Julien Bianchi and Hannes Rosenögger)
- Fix overflow at sidebar when have several items
- Add notes for label changes in issue and merge requests
- Show tags in commit view (Hannes Rosenögger)
......@@ -165,7 +181,7 @@ v 7.8.0
- Add a commit calendar to the user profile (Hannes Rosenögger)
- Submit comment on command-enter
- Notify all members of a group when that group is mentioned in a comment, for example: `@gitlab-org` or `@sales`.
- Extend issue clossing pattern to include "Resolve", "Resolves", "Resolved", "Resolving" and "Close"
- Extend issue clossing pattern to include "Resolve", "Resolves", "Resolved", "Resolving" and "Close" (Julien Bianchi and Hannes Rosenögger)
- Fix long broadcast message cut-off on left sidebar (Visay Keo)
- Add Project Avatars (Steven Thonus and Hannes Rosenögger)
- Password reset token validity increased from 2 hours to 2 days since it is also send on account creation.
......
......@@ -188,7 +188,7 @@ GEM
dotenv (>= 0.7)
thor (>= 0.13.6)
formatador (0.2.4)
gemnasium-gitlab-service (0.2.5)
gemnasium-gitlab-service (0.2.4)
rugged (~> 0.21)
gemojione (2.0.0)
json
......@@ -516,7 +516,7 @@ GEM
rubyntlm (0.5.0)
rubypants (0.2.0)
rugged (0.21.4)
rugments (1.0.0.beta5)
rugments (1.0.0.beta6)
safe_yaml (0.9.7)
sanitize (2.1.0)
nokogiri (>= 1.4.4)
......
......@@ -7,7 +7,7 @@ class @calendar
constructor: (timestamps, starting_year, starting_month, calendar_activities_path) ->
cal = new CalHeatMap()
cal.init
itemName: ["commit"]
itemName: ["contribution"]
data: timestamps
start: new Date(starting_year, starting_month)
domainLabelFormat: "%b"
......@@ -27,7 +27,6 @@ class @calendar
legendCellPadding: 3
onClick: (date, count) ->
formated_date = date.getFullYear() + "-" + (date.getMonth()+1) + "-" + date.getDate()
$(".calendar_commit_activity").fadeOut 400
$.ajax
url: calendar_activities_path
data:
......@@ -36,6 +35,4 @@ class @calendar
dataType: "html"
success: (data) ->
$(".user-calendar-activities").html data
$(".calendar_commit_activity").find(".js-toggle-content").hide()
$(".calendar_commit_activity").fadeIn 400
......@@ -37,8 +37,6 @@ class @Diff
)
)
$('.diff-header').stick_in_parent(recalc_every: 1, offset_top: $('.navbar').height())
lineNumbers: (line) ->
return ([0, 0]) unless line.children().length
lines = line.children().slice(0, 2)
......
......@@ -97,6 +97,7 @@ class Dispatcher
new ProjectFork()
when 'users:show'
new User()
new Activities()
when 'projects:group_links:index'
new GroupsSelect()
when 'admin:emails:show'
......
......@@ -113,8 +113,14 @@ class @MergeRequest
allowed_states = ["failed", "canceled", "running", "pending", "success"]
if state in allowed_states
$('.ci_widget.ci-' + state).show()
switch state
when "failed", "canceled"
@setMergeButtonClass('btn-danger')
when "running", "pending"
@setMergeButtonClass('btn-warning')
else
$('.ci_widget.ci-error').show()
@setMergeButtonClass('btn-danger')
showCiCoverage: (coverage) ->
cov_html = $('<span>')
......@@ -144,6 +150,9 @@ class @MergeRequest
this.$('.merge-in-progress').hide()
this.$('.automerge_widget.already_cannot_be_merged').show()
setMergeButtonClass: (css_class) ->
$('.accept_merge_request').removeClass("btn-create").addClass(css_class)
mergeInProgress: ->
$.ajax
type: 'GET'
......
......@@ -25,7 +25,7 @@ class @ProjectUsersSelect
initSelection: (element, callback) ->
id = $(element).val()
if id isnt ""
if id != "" && id != "-1"
Api.user(id, callback)
......@@ -44,10 +44,7 @@ class @ProjectUsersSelect
else
avatar = gon.default_avatar_url
if user.id == ''
avatarMarkup = ''
else
avatarMarkup = "<div class='user-image'><img class='avatar s24' src='#{avatar}'></div>"
avatarMarkup = "<div class='user-image'><img class='avatar s24' src='#{avatar}'></div>"
"<div class='user-result'>
#{avatarMarkup}
......
......@@ -152,6 +152,8 @@
*/
.panel {
.panel-heading {
font-weight: bold;
.panel-head-actions {
position: relative;
top: -5px;
......
.user-calendar-activities {
.calendar_commit_activity {
padding: 5px 0 0;
}
.calendar_onclick_hr {
padding: 0;
margin: 10px 0;
}
.calendar_commit_date {
color: #999;
}
.calendar_activity_summary {
font-size: 14px;
}
.str-truncated {
max-width: 70%;
......@@ -31,14 +18,6 @@
background-color: #ddd;
}
}
.commit-row-message {
color: #333;
&:hover {
color: #444;
text-decoration: underline;
}
}
}
/**
* This overwrites the default values of the cal-heatmap gem
......
......@@ -15,6 +15,11 @@
word-break: break-all;
margin-right: 200px;
display: block;
.file-mode {
margin-left: 10px;
color: #777;
}
}
.diff-btn-group {
......@@ -34,11 +39,6 @@
font-family: $monospace_font;
font-size: smaller;
}
.file-mode {
font-family: $monospace_font;
margin-left: 10px;
}
}
.diff-content {
overflow: auto;
......
......@@ -137,30 +137,15 @@
background-color: #F1FAF1;
}
&.ci-pending {
color: #548;
border-color: #548;
background-color: #F4F1FA;
}
&.ci-pending,
&.ci-running {
color: $gl-warning;
border-color: $gl-warning;
background-color: #FAF5F1;
}
&.ci-failed {
color: $gl-danger;
border-color: $gl-danger;
background-color: #FAF1F1;
}
&.ci-canceled {
color: $gl-warning;
border-color: $gl-danger;
background-color: #FAF5F1;
}
&.ci-failed,
&.ci-canceled,
&.ci-error {
color: $gl-danger;
border-color: $gl-danger;
......
/**
* Modern GitLab UI theme
* Blue GitLab UI theme
*/
.ui_blue {
@include dark-theme(#BECDE9, #2980b9, #1970a9, #096099);
......
......@@ -46,7 +46,9 @@ class Admin::ServicesController < Admin::ApplicationController
:user_key, :device, :priority, :sound, :bamboo_url, :username, :password,
:build_key, :server, :teamcity_url, :build_type,
:description, :issues_url, :new_issue_url, :restrict_to_branch,
:send_from_committer_email, :disable_diffs
:send_from_committer_email, :disable_diffs,
:push_events, :tag_push_events, :note_events, :issues_events,
:merge_requests_events
])
end
end
......@@ -257,7 +257,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
def allowed_to_push_code?(project, branch)
::Gitlab::GitAccess.can_push_to_branch?(current_user, project, branch)
::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(branch)
end
def merge_request_params
......
......@@ -4,10 +4,7 @@ class UsersController < ApplicationController
layout :determine_layout
def show
@contributed_projects = Project.
where(id: authorized_projects_ids & @user.contributed_projects_ids).
in_group_namespace.
includes(:namespace).
@contributed_projects = contributed_projects.joined(@user).
reject(&:forked?)
@projects = @user.personal_projects.
......@@ -16,24 +13,26 @@ class UsersController < ApplicationController
# Collect only groups common for both users
@groups = @user.groups & GroupsFinder.new.execute(current_user)
# Get user activity feed for projects common for both users
@events = @user.recent_events.
where(project_id: authorized_projects_ids).
with_associations.limit(30)
@title = @user.name
@title_url = user_path(@user)
respond_to do |format|
format.html
format.atom { render layout: false }
format.atom do
load_events
render layout: false
end
format.json do
load_events
pager_json("events/_events", @events.count)
end
end
end
def calendar
projects = Project.where(id: authorized_projects_ids & @user.contributed_projects_ids)
calendar = Gitlab::CommitsCalendar.new(projects, @user)
calendar = contributions_calendar
@timestamps = calendar.timestamps
@starting_year = calendar.starting_year
@starting_month = calendar.starting_month
......@@ -42,20 +41,13 @@ class UsersController < ApplicationController
end
def calendar_activities
projects = Project.where(id: authorized_projects_ids & @user.contributed_projects_ids)
@calendar_date = Date.parse(params[:date]) rescue nil
@events = []
date = Date.parse(params[:date]) rescue nil
if date
@calendar_activities = Gitlab::CommitsCalendar.get_commits_for_date(projects, @user, date)
else
@calendar_activities = {}
if @calendar_date
@events = contributions_calendar.events_by_date(@calendar_date)
end
# get the total number of unique commits
@commit_count = @calendar_activities.values.flatten.map(&:id).uniq.count
@calendar_date = date
render 'calendar_activities', layout: false
end
......@@ -82,4 +74,24 @@ class UsersController < ApplicationController
@authorized_projects_ids ||=
ProjectsFinder.new.execute(current_user).pluck(:id)
end
def contributed_projects
@contributed_projects = Project.
where(id: authorized_projects_ids & @user.contributed_projects_ids).
includes(:namespace)
end
def contributions_calendar
@contributions_calendar ||= Gitlab::ContributionsCalendar.
new(contributed_projects.reject(&:forked?), @user)
end
def load_events
# Get user activity feed for projects common for both users
@events = @user.recent_events.
where(project_id: authorized_projects_ids).
with_associations
@events = @events.limit(20).offset(params[:offset] || 0)
end
end
......@@ -12,7 +12,7 @@ module BranchesHelper
def can_push_branch?(project, branch_name)
return false unless project.repository.branch_names.include?(branch_name)
::Gitlab::GitAccess.can_push_to_branch?(current_user, project, branch_name)
::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(branch_name)
end
def can_rebase?(project, branch_name)
......
......@@ -29,7 +29,7 @@ module GitlabMarkdownHelper
end
def markdown(text, options={})
unless (@markdown and options == @options)
unless @markdown && options == @options
@options = options
gitlab_renderer = Redcarpet::Render::GitlabHTML.new(self,
user_color_scheme_class,
......@@ -182,7 +182,7 @@ module GitlabMarkdownHelper
def file_exists?(path)
return false if path.nil?
return @repository.blob_at(current_sha, path).present? || @repository.tree(current_sha, path).entries.any?
@repository.blob_at(current_sha, path).present? || @repository.tree(current_sha, path).entries.any?
end
# Check if the path is pointing to a directory(tree) or a file(blob)
......@@ -190,7 +190,7 @@ module GitlabMarkdownHelper
def local_path(path)
return "tree" if @repository.tree(current_sha, path).entries.any?
return "raw" if @repository.blob_at(current_sha, path).image?
return "blob"
"blob"
end
def current_sha
......
......@@ -58,22 +58,11 @@ module IssuesHelper
end
def bulk_update_milestone_options
options_for_select(['None (backlog)']) +
options_for_select([['None (backlog)', -1]]) +
options_from_collection_for_select(project_active_milestones, 'id',
'title', params[:milestone_id])
end
def bulk_update_assignee_options(project = @project)
options_for_select(['None (unassigned)']) +
options_from_collection_for_select(project.team.members, 'id',
'name', params[:assignee_id])
end
def assignee_options(object, project = @project)
options_from_collection_for_select(project.team.members.sort_by(&:name),
'id', 'name', object.assignee_id)
end
def milestone_options(object)
options_from_collection_for_select(object.project.milestones.active,
'id', 'title', object.milestone_id)
......
......@@ -17,7 +17,7 @@ module MergeRequestsHelper
end
def new_mr_from_push_event(event, target_project)
return {
{
merge_request: {
source_project_id: event.project.id,
target_project_id: target_project.id,
......
......@@ -146,6 +146,10 @@ module ProjectsHelper
nav_tabs << feature if project.send :"#{feature}_enabled"
end
if project.issues_enabled || project.merge_requests_enabled
nav_tabs << [:milestones, :labels]
end
nav_tabs.flatten
end
......
......@@ -23,9 +23,9 @@ module SearchHelper
# Autocomplete results for various settings pages
def default_autocomplete
[
{ label: "My Profile settings", url: profile_path },
{ label: "My SSH Keys", url: profile_keys_path },
{ label: "My Dashboard", url: root_path },
{ label: "Profile settings", url: profile_path },
{ label: "SSH Keys", url: profile_keys_path },
{ label: "Dashboard", url: root_path },
{ label: "Admin Section", url: admin_root_path },
]
end
......
......@@ -49,7 +49,7 @@ module SubmoduleHelper
def standard_links(host, namespace, project, commit)
base = [ 'https://', host, '/', namespace, '/', project ].join('')
return base, [ base, '/tree/', commit ].join('')
[base, [ base, '/tree/', commit ].join('')]
end
def relative_self_links(url, commit)
......@@ -58,7 +58,10 @@ module SubmoduleHelper
else
base = [ @project.group.path, '/', url[/([^\/]*)\.git/, 1] ].join('')
end
return namespace_project_path(base.namespace, base),
[
namespace_project_path(base.namespace, base),
namespace_project_tree_path(base.namespace, base, commit)
]
end
end
......@@ -56,7 +56,7 @@ module TreeHelper
ref ||= @ref
return false unless project.repository.branch_names.include?(ref)
::Gitlab::GitAccess.can_push_to_branch?(current_user, project, ref)
::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref)
end
def tree_breadcrumbs(tree, max_links = 2)
......
......@@ -16,31 +16,69 @@ module Emails
subject: subject("Project was moved"))
end
def repository_push_email(project_id, recipient, author_id, branch, compare, reverse_compare = false, send_from_committer_email = false, disable_diffs = false)
def repository_push_email(project_id, recipient, author_id: nil,
ref: nil,
action: nil,
compare: nil,
reverse_compare: false,
send_from_committer_email: false,
disable_diffs: false)
unless author_id && ref && action
raise ArgumentError, "missing keywords: author_id, ref, action"
end
@project = Project.find(project_id)
@author = User.find(author_id)
@reverse_compare = reverse_compare
@compare = compare
@commits = Commit.decorate(compare.commits)
@diffs = compare.diffs
@branch = Gitlab::Git.ref_name(branch)
@ref_name = Gitlab::Git.ref_name(ref)
@ref_type = Gitlab::Git.tag_ref?(ref) ? "tag" : "branch"
@action = action
@disable_diffs = disable_diffs
@subject = "[#{@project.path_with_namespace}][#{@branch}] "
if @compare
@commits = Commit.decorate(compare.commits)
@diffs = compare.diffs
end
@action_name =
case action
when :create
"pushed new"
when :delete
"deleted"
else
"pushed to"
end
@subject = "[#{@project.path_with_namespace}]"
@subject << "[#{@ref_name}]" if action == :push
@subject << " "
if action == :push
if @commits.length > 1
@target_url = namespace_project_compare_url(@project.namespace,
@project,
from: Commit.new(@compare.base),
to: Commit.new(@compare.head))
@subject << "Deleted " if @reverse_compare
@subject << "#{@commits.length} commits: #{@commits.first.title}"
else
@target_url = namespace_project_commit_url(@project.namespace,
@project, @commits.first)
if @commits.length > 1
@target_url = namespace_project_compare_url(@project.namespace,
@project,
from: Commit.new(@compare.base),
to: Commit.new(@compare.head))
@subject << "Deleted " if @reverse_compare
@subject << "#{@commits.length} commits: #{@commits.first.title}"
@subject << "Deleted 1 commit: " if @reverse_compare
@subject << @commits.first.title
end
else
@target_url = namespace_project_commit_url(@project.namespace,
@project, @commits.first)
unless action == :delete
@target_url = namespace_project_tree_url(@project.namespace,
@project, @ref_name)
end
@subject << "Deleted 1 commit: " if @reverse_compare
@subject << @commits.first.title
subject_action = @action_name.dup
subject_action[0] = subject_action[0].capitalize
@subject << "#{subject_action} #{@ref_type} #{@ref_name}"
end
@disable_footer = true
......
......@@ -149,7 +149,7 @@ class Notify < ActionMailer::Base
headers['References'] = message_id(model)
headers['X-GitLab-Project'] = "#{@project.name} | " if @project
if (headers[:subject])
if headers[:subject]
headers[:subject].prepend('Re: ')
end
......
......@@ -5,7 +5,7 @@
# Used by MergeRequest and Issue
module Taskable
TASK_PATTERN_MD = /^(?<bullet> *[*-] *)\[(?<checked>[ xX])\]/.freeze
TASK_PATTERN_HTML = /^<li>\[(?<checked>[ xX])\]/.freeze
TASK_PATTERN_HTML = /^<li>(?<p_tag>\s*<p>)?\[(?<checked>[ xX])\]/.freeze
# Change the state of a task list item for this Taskable. Edit the object's
# description by finding the nth task item and changing its checkbox
......
......@@ -55,6 +55,12 @@ class Event < ActiveRecord::Base
order('id DESC').limit(100).
update_all(updated_at: Time.now)
end
def contributions
where("action = ? OR (target_type in (?) AND action in (?))",
Event::PUSHED, ["MergeRequest", "Issue"],
[Event::CREATED, Event::CLOSED, Event::MERGED])
end
end
def proper?
......
......@@ -108,6 +108,15 @@ class MergeRequest < ActiveRecord::Base
state :unchecked
state :can_be_merged
state :cannot_be_merged
around_transition do |merge_request, transition, block|
merge_request.record_timestamps = false
begin
block.call
ensure
merge_request.record_timestamps = true
end
end
end
validates :source_project, presence: true, unless: :allow_broken
......
......@@ -48,6 +48,7 @@ class Note < ActiveRecord::Base
scope :inline, ->{ where("line_code IS NOT NULL") }
scope :not_inline, ->{ where(line_code: [nil, '']) }
scope :system, ->{ where(system: true) }
scope :user, ->{ where(system: false) }
scope :common, ->{ where(noteable_type: ["", nil]) }
scope :fresh, ->{ order(created_at: :asc, id: :asc) }
scope :inc_author_project, ->{ includes(:project, :author) }
......
class ProjectContributions
attr_reader :project, :user
def initialize(project, user)
@project, @user = project, user
end
def commits_log
repository = project.repository
if !repository.exists? || repository.empty?
return {}
end
Rails.cache.fetch(cache_key) do
repository.commits_per_day_for_user(user)
end
end
def user_commits_on_date(date)
repository = @project.repository
if !repository.exists? || repository.empty?
return []
end
commits = repository.commits_by_user_on_date_log(@user, date)
end
def cache_key
"#{Date.today.to_s}-commits-log-#{project.id}-#{user.email}"
end
end
......@@ -82,7 +82,7 @@ automatically inspected. Leave blank to include all branches.'
branch_restriction = restrict_to_branch.to_s
# check the branch restriction is poplulated and branch is not included
if branch_restriction.length > 0 && branch_restriction.index(branch) == nil
if branch_restriction.length > 0 && branch_restriction.index(branch).nil?
return
end
......
......@@ -36,13 +36,19 @@ class EmailsOnPushService < Service
end
def supported_events
%w(push)
%w(push tag_push)
end
def execute(push_data)
return unless supported_events.include?(push_data[:object_kind])
EmailsOnPushWorker.perform_async(project_id, recipients, push_data, send_from_committer_email?, disable_diffs?)
EmailsOnPushWorker.perform_async(
project_id,
recipients,
push_data,
send_from_committer_email: send_from_committer_email?,
disable_diffs: disable_diffs?
)
end
def send_from_committer_email?
......
......@@ -104,7 +104,7 @@ class ProjectWiki
def page_title_and_dir(title)
title_array = title.split("/")
title = title_array.pop
[title.gsub(/\.[^.]*$/, ""), title_array.join("/")]
[title, title_array.join("/")]
end
def search_files(query)
......
......@@ -149,41 +149,6 @@ class Repository
end
end
def timestamps_by_user_log(user)
author_emails = '(' + user.all_emails.map{ |e| Regexp.escape(e) }.join('|') + ')'
args = %W(git log -E --author=#{author_emails} --since=#{(Date.today - 1.year).to_s} --branches --pretty=format:%cd --date=short)
dates = Gitlab::Popen.popen(args, path_to_repo).first.split("\n")
if dates.present?
dates
else
[]
end
end
def commits_by_user_on_date_log(user, date)
# format the date string for git
start_date = date.strftime("%Y-%m-%d 00:00:00")
end_date = date.strftime("%Y-%m-%d 23:59:59")
author_emails = '(' + user.all_emails.map{ |e| Regexp.escape(e) }.join('|') + ')'
args = %W(git log -E --author=#{author_emails} --after=#{start_date.to_s} --until=#{end_date.to_s} --branches --pretty=format:%h)
commits = Gitlab::Popen.popen(args, path_to_repo).first.split("\n")
commits.map! do |commit_id|
commit(commit_id)
end
end
def commits_per_day_for_user(user)
timestamps_by_user_log(user).
group_by { |commit_date| commit_date }.
inject({}) do |hash, (timestamp_date, commits)|
hash[timestamp_date] = commits.count
hash
end
end
def lookup_cache
@lookup_cache ||= {}
end
......
......@@ -110,6 +110,7 @@ class User < ActiveRecord::Base
has_many :notes, dependent: :destroy, foreign_key: :author_id
has_many :merge_requests, dependent: :destroy, foreign_key: :author_id
has_many :events, dependent: :destroy, foreign_key: :author_id, class_name: "Event"
has_many :subscriptions, dependent: :destroy
has_many :recent_events, -> { order "id DESC" }, foreign_key: :author_id, class_name: "Event"
has_many :assigned_issues, dependent: :destroy, foreign_key: :assignee_id, class_name: "Issue"
has_many :assigned_merge_requests, dependent: :destroy, foreign_key: :assignee_id, class_name: "MergeRequest"
......@@ -619,13 +620,10 @@ class User < ActiveRecord::Base
end
def contributed_projects_ids
Event.where(author_id: self).
Event.contributions.where(author_id: self).
where("created_at > ?", Time.now - 1.year).
where("action = :pushed OR (target_type = 'MergeRequest' AND action = :created)",
pushed: Event::PUSHED, created: Event::CREATED).
reorder(project_id: :desc).
select(:project_id).
uniq
.map(&:project_id)
uniq.map(&:project_id)
end
end
......@@ -179,7 +179,8 @@ class WikiPage
if valid? && project_wiki.send(method, *args)
page_details = if method == :update_page
@page.path
# Use url_path instead of path to omit format extension
@page.url_path
else
title
end
......
......@@ -3,7 +3,7 @@ require_relative "base_service"
module Files
class CreateService < BaseService
def execute
allowed = Gitlab::GitAccess.can_push_to_branch?(current_user, project, ref)
allowed = Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref)
unless allowed
return error("You are not allowed to create file in this branch")
......
......@@ -3,7 +3,7 @@ require_relative "base_service"
module Files
class DeleteService < BaseService
def execute
allowed = ::Gitlab::GitAccess.can_push_to_branch?(current_user, project, ref)
allowed = ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref)
unless allowed
return error("You are not allowed to push into this branch")
......
......@@ -3,7 +3,7 @@ require_relative "base_service"
module Files
class UpdateService < BaseService
def execute
allowed = ::Gitlab::GitAccess.can_push_to_branch?(current_user, project, ref)
allowed = ::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(ref)
unless allowed
return error("You are not allowed to push into this branch")
......
......@@ -4,9 +4,9 @@ module Issues
issues_ids = params.delete(:issues_ids).split(",")
issue_params = params
issue_params.delete(:state_event) unless issue_params[:state_event].present?
issue_params.delete(:milestone_id) unless issue_params[:milestone_id].present?
issue_params.delete(:assignee_id) unless issue_params[:assignee_id].present?
issue_params.delete(:state_event) unless issue_params[:state_event].present?
issue_params.delete(:milestone_id) unless issue_params[:milestone_id].present?
issue_params.delete(:assignee_id) unless issue_params[:assignee_id].present?
issues = Issue.where(id: issues_ids)
issues.each do |issue|
......
......@@ -14,6 +14,9 @@ module Issues
issue.update_nth_task(params[:task_num].to_i, false)
end
params[:assignee_id] = "" if params[:assignee_id] == "-1"
params[:milestone_id] = "" if params[:milestone_id] == "-1"
old_labels = issue.labels.to_a
if params.present? && issue.update_attributes(params.except(:state_event,
......
......@@ -53,7 +53,7 @@ module MergeRequests
if merge_request.source_branch == @branch_name || force_push?
merge_request.reload_code
update_merge_request(merge_request)
merge_request.mark_as_unchecked
else
mr_commit_ids = merge_request.commits.map(&:id)
push_commit_ids = @commits.map(&:id)
......@@ -61,20 +61,14 @@ module MergeRequests
if matches.any?
merge_request.reload_code
update_merge_request(merge_request)
merge_request.mark_as_unchecked
else
update_merge_request(merge_request)
merge_request.mark_as_unchecked
end
end
end
end
def update_merge_request(merge_request)
MergeRequests::UpdateService.new(
merge_request.target_project,
@current_user, merge_status: 'unchecked').execute(merge_request)
end
# Add comment about pushing new commits to merge requests
def comment_mr_with_commits
merge_requests = @project.origin_merge_requests.opened.where(source_branch: @branch_name).to_a
......
......@@ -23,6 +23,9 @@ module MergeRequests
merge_request.update_nth_task(params[:task_num].to_i, false)
end
params[:assignee_id] = "" if params[:assignee_id] == "-1"
params[:milestone_id] = "" if params[:milestone_id] == "-1"
old_labels = merge_request.labels.to_a
if params.present? && merge_request.update_attributes(
......
......@@ -21,13 +21,11 @@
.form-group.js-toggle-colors-container.hide
= f.label :color, "Background Color", class: 'control-label'
.col-sm-10
= f.text_field :color, placeholder: "#AA33EE", class: "form-control"
.light 6 character hex values starting with a # sign.
= f.color_field :color, value: "#AA33EE", class: "form-control"
.form-group.js-toggle-colors-container.hide
= f.label :font, "Font Color", class: 'control-label'
.col-sm-10
= f.text_field :font, placeholder: "#224466", class: "form-control"
.light 6 character hex values starting with a # sign.
= f.color_field :font, value: "#224466", class: "form-control"
.form-group
= f.label :starts_at, class: 'control-label'
.col-sm-10.datetime-controls
......
......@@ -14,6 +14,11 @@
= preserve do
= markdown @service.help
.form-group
= f.label :active, "Active", class: "control-label"
.col-sm-10
= f.check_box :active
- if @service.supported_events.length > 1
.form-group
= f.label :url, "Trigger", class: 'control-label'
......@@ -34,6 +39,14 @@
%strong Tag push events
%p.light
This url will be triggered when a new tag is pushed to the repository
- if @service.supported_events.include?("note")
%div
= f.check_box :note_events, class: 'pull-left'
.prepend-left-20
= f.label :note_events, class: 'list-label' do
%strong Comments
%p.light
This url will be triggered when someone adds a comment
- if @service.supported_events.include?("issue")
%div
= f.check_box :issues_events, class: 'pull-left'
......
......@@ -6,4 +6,4 @@
<p>You can confirm your account through the link below:</p>
<% end %>
<p><%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %></p>
<p><%= link_to 'Confirm your account', confirmation_url(@resource, confirmation_token: @token) %></p>
......@@ -2,7 +2,7 @@
<p>Someone has requested a link to change your password, and you can do this through the link below.</p>
<p><%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %></p>
<p><%= link_to 'Change your password', edit_password_url(@resource, reset_password_token: @token) %></p>
<p>If you didn't request this, please ignore this email.</p>
<p>Your password won't change until you access the link above and create a new one.</p>
......@@ -4,4 +4,4 @@
<p>Click the link below to unlock your account:</p>
<p><%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) %></p>
<p><%= link_to 'Unlock your account', unlock_url(@resource, unlock_token: @token) %></p>
......@@ -11,7 +11,7 @@
%div
= f.password_field :password_confirmation, class: "form-control bottom", placeholder: "Confirm new password", required: true
.clearfix
= f.submit "Change my password", class: "btn btn-primary"
= f.submit "Change your password", class: "btn btn-primary"
.clearfix.prepend-top-20
%p
......
......@@ -21,8 +21,8 @@
<div><%= f.submit "Update", class: "input_button" %></div>
<% end %>
<h3>Cancel my account</h3>
<h3>Cancel your account</h3>
<p>Unhappy? <%= link_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %>.</p>
<p>Unhappy? <%= link_to "Cancel your account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %>.</p>
<%= link_to "Back", :back %>
......@@ -53,7 +53,7 @@
%code .panel .well-list
.panel.panel-default
.panel-heading My list
.panel-heading Your list
%ul.well-list
%li
One item
......
......@@ -25,7 +25,7 @@
= link_to explore_root_path, title: "Explore", class: 'has_bottom_tooltip', 'data-original-title' => 'Public area' do
%i.fa.fa-globe
%li
= link_to user_snippets_path(current_user), title: "My snippets", class: 'has_bottom_tooltip', 'data-original-title' => 'My snippets' do
= link_to user_snippets_path(current_user), title: "Your snippets", class: 'has_bottom_tooltip', 'data-original-title' => 'Your snippets' do
%i.fa.fa-clipboard
- if current_user.is_admin?
%li
......
......@@ -44,11 +44,12 @@
%span
Graphs
= nav_link(controller: :milestones) do
= link_to namespace_project_milestones_path(@project.namespace, @project), title: 'Milestones' do
%i.fa.fa-clock-o
%span
Milestones
- if project_nav_tab? :milestones
= nav_link(controller: :milestones) do
= link_to namespace_project_milestones_path(@project.namespace, @project), title: 'Milestones' do
%i.fa.fa-clock-o
%span
Milestones
- if project_nav_tab? :issues
= nav_link(controller: :issues) do
......@@ -67,11 +68,12 @@
Merge Requests
%span.count.merge_counter= @project.merge_requests.opened.count
= nav_link(controller: :labels) do
= link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels' do
%i.fa.fa-tags
%span
Labels
- if project_nav_tab? :labels
= nav_link(controller: :labels) do
= link_to namespace_project_labels_path(@project.namespace, @project), title: 'Labels' do
%i.fa.fa-tags
%span
Labels
- if project_nav_tab? :wiki
= nav_link(controller: :wikis) do
......
%h3 #{@author.name} pushed to #{@branch} at #{link_to @project.name_with_namespace, namespace_project_url(@project.namespace, @project)}
%h3 #{@author.name} #{@action_name} #{@ref_type} #{@ref_name} at #{link_to @project.name_with_namespace, namespace_project_url(@project.namespace, @project)}
- if @reverse_compare
%p
%strong WARNING:
The push did not contain any new commits, but force pushed to delete the commits and changes below.
- if @compare
- if @reverse_compare
%p
%strong WARNING:
The push did not contain any new commits, but force pushed to delete the commits and changes below.
%h4
= @reverse_compare ? "Deleted commits:" : "Commits:"
%h4
= @reverse_compare ? "Deleted commits:" : "Commits:"
%ul
- @commits.each do |commit|
%li
%strong #{link_to commit.short_id, namespace_project_commit_url(@project.namespace, @project, commit)}
%div
%span by #{commit.author_name}
%i at #{commit.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ")}
%pre.commit-message
= commit.safe_message
%ul
- @commits.each do |commit|
%li
%strong #{link_to commit.short_id, namespace_project_commit_url(@project.namespace, @project, commit)}
%div
%span by #{commit.author_name}
%i at #{commit.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ")}
%pre.commit-message
= commit.safe_message
%h4 #{pluralize @diffs.count, "changed file"}:
%h4 #{pluralize @diffs.count, "changed file"}:
%ul
- @diffs.each_with_index do |diff, i|
%li.file-stats
%a{href: "#{@target_url if @disable_diffs}#diff-#{i}" }
- if diff.deleted_file
%span.deleted-file
&minus;
%ul
- @diffs.each_with_index do |diff, i|
%li.file-stats
%a{href: "#{@target_url if @disable_diffs}#diff-#{i}" }
- if diff.deleted_file
%span.deleted-file
&minus;
= diff.old_path
- elsif diff.renamed_file
= diff.old_path
- elsif diff.renamed_file
= diff.old_path
&rarr;
= diff.new_path
- elsif diff.new_file
%span.new-file
&plus;
&rarr;
= diff.new_path
- else
= diff.new_path
- unless @disable_diffs
%h4 Changes:
- @diffs.each_with_index do |diff, i|
%li{id: "diff-#{i}"}
%a{href: @target_url + "#diff-#{i}"}
- if diff.deleted_file
%strong
= diff.old_path
deleted
- elsif diff.renamed_file
%strong
= diff.old_path
&rarr;
%strong
- elsif diff.new_file
%span.new-file
&plus;
= diff.new_path
- else
= diff.new_path
- else
%strong
= diff.new_path
%hr
%pre
= color_email_diff(diff.diff)
%br
- if @compare.timeout
%h5 Huge diff. To prevent performance issues changes are hidden
- unless @disable_diffs
%h4 Changes:
- @diffs.each_with_index do |diff, i|
%li{id: "diff-#{i}"}
%a{href: @target_url + "#diff-#{i}"}
- if diff.deleted_file
%strong
= diff.old_path
deleted
- elsif diff.renamed_file
%strong
= diff.old_path
&rarr;
%strong
= diff.new_path
- else
%strong
= diff.new_path
%hr
%pre
= color_email_diff(diff.diff)
%br
- if @compare.timeout
%h5 Huge diff. To prevent performance issues changes are hidden
#{@author.name} pushed to #{@branch} at #{@project.name_with_namespace}
\
\
- if @reverse_compare
WARNING: The push did not contain any new commits, but force pushed to delete the commits and changes below.
#{@author.name} #{@action_name} #{@ref_type} #{@ref_name} at #{@project.name_with_namespace}
- if @compare
\
\
= @reverse_compare ? "Deleted commits:" : "Commits:"
- @commits.each do |commit|
#{commit.short_id} by #{commit.author_name} at #{commit.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ")}
#{commit.safe_message}
\- - - - -
\
\
#{pluralize @diffs.count, "changed file"}:
\
- @diffs.each do |diff|
- if diff.deleted_file
\- − #{diff.old_path}
- elsif diff.renamed_file
\- #{diff.old_path}#{diff.new_path}
- elsif diff.new_file
\- + #{diff.new_path}
- else
\- #{diff.new_path}
- unless @disable_diffs
- if @reverse_compare
WARNING: The push did not contain any new commits, but force pushed to delete the commits and changes below.
\
\
= @reverse_compare ? "Deleted commits:" : "Commits:"
- @commits.each do |commit|
#{commit.short_id} by #{commit.author_name} at #{commit.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ")}
#{commit.safe_message}
\- - - - -
\
\
#{pluralize @diffs.count, "changed file"}:
\
Changes:
- @diffs.each do |diff|
\
\=====================================
- if diff.deleted_file
#{diff.old_path} deleted
\- − #{diff.old_path}
- elsif diff.renamed_file
#{diff.old_path}#{diff.new_path}
\- #{diff.old_path}#{diff.new_path}
- elsif diff.new_file
\- + #{diff.new_path}
- else
= diff.new_path
\=====================================
!= diff.diff
- if @compare.timeout
\
\
Huge diff. To prevent performance issues it was hidden
\
\
View it on GitLab: #{@target_url}
\- #{diff.new_path}
- unless @disable_diffs
\
\
Changes:
- @diffs.each do |diff|
\
\=====================================
- if diff.deleted_file
#{diff.old_path} deleted
- elsif diff.renamed_file
#{diff.old_path}#{diff.new_path}
- else
= diff.new_path
\=====================================
!= diff.diff
- if @compare.timeout
\
\
Huge diff. To prevent performance issues it was hidden
- if @target_url
\
\
View it on GitLab: #{@target_url}
%h3.page-title
My Account History
Your Account History
%p.light
All events created by your account are listed below.
%hr
......
......@@ -3,8 +3,6 @@
.pull-right
= link_to "Add SSH Key", new_profile_key_path, class: "btn btn-new"
%p.light
My SSH keys: #{@keys.count}
%br
Before you can add an SSH key you need to
= link_to "generate it.", help_page_path("ssh", "README")
%hr
......
......@@ -19,7 +19,7 @@
.fork-buttons
- if current_user && can?(current_user, :fork_project, @project) && @project.namespace != current_user.namespace
- if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2
= link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to my fork' do
= link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork' do
= link_to_toggle_fork
- else
= link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project" do
......
......@@ -13,7 +13,7 @@
- note_count = @note_counts.fetch(commit.id, 0)
- else
- notes = project.notes.for_commit_id(commit.id)
- note_count = notes.count
- note_count = notes.user.count
- if note_count > 0
%span.light
......
.row.prepend-top-20.append-bottom-10
.col-md-8
= render 'projects/diffs/stats', diffs: diffs
.col-md-4
.btn-group.pull-right
.prepend-top-20.append-bottom-20
.pull-right
.btn-group
= inline_diff_btn
= parallel_diff_btn
= render 'projects/diffs/stats', diffs: diffs
- if show_diff_size_warning?(diffs)
= render 'projects/diffs/warning', diffs: diffs
......@@ -19,3 +18,6 @@
Failed to collect changes
%p
Maybe diff is really big and operation failed with timeout. Try to get diff locally
:coffeescript
$('.files .diff-header').stick_in_parent(recalc_every: 1, offset_top: $('.navbar').height())
......@@ -13,12 +13,13 @@
- submodule_item = project.repository.blob_at(@commit.id, diff_file.file_path)
= submodule_link(submodule_item, @commit.id)
- else
- if diff_file.renamed_file
%span= "#{diff_file.old_path} renamed to #{diff_file.new_path}"
- else
%span= diff_file.new_path
- if diff_file.mode_changed?
%span.file-mode= "#{diff_file.diff.a_mode}#{diff_file.diff.b_mode}"
%span
- if diff_file.renamed_file
= "#{diff_file.old_path} renamed to #{diff_file.new_path}"
- else
= diff_file.new_path
- if diff_file.mode_changed?
%span.file-mode= "#{diff_file.diff.a_mode}#{diff_file.diff.b_mode}"
.diff-btn-group
- if blob.text?
......
......@@ -16,7 +16,7 @@
- else
%td.old_line
= link_to raw(type == "new" ? "&nbsp;" : line_old), "##{line_code}", id: line_code
- if @comments_allowed
- if @comments_allowed && can?(current_user, :write_note, @project)
= link_to_new_diff_note(line_code)
%td.new_line{data: {linenumber: line.new_pos}}
= link_to raw(type == "old" ? "&nbsp;" : line.new_pos) , "##{line_code}", id: line_code
......
......@@ -10,11 +10,12 @@
- if issue.closed?
%span
CLOSED
- if issue.notes.any?
- note_count = issue.notes.user.count
- if note_count > 0
&nbsp;
%span
%i.fa.fa-comments
= issue.notes.count
= note_count
.issue-info
= link_to "##{issue.iid}", issue_path(issue), class: "light"
......
......@@ -16,9 +16,9 @@
.col-sm-10
.input-group
.input-group-addon.label-color-preview &nbsp;
= f.color_field :color, placeholder: "#AA33EE", class: "form-control"
= f.color_field :color, value: "#AA33EE", class: "form-control"
.help-block
6 character hex values starting with a # sign.
Choose any color.
%br
Or you can choose one of suggested colors below
......
......@@ -16,11 +16,12 @@
%span.label-branch<
%i.fa.fa-code-fork
%span= merge_request.target_branch
- if merge_request.notes.any?
- note_count = merge_request.mr_and_commit_notes.user.count
- if note_count > 0
&nbsp;
%span
%i.fa.fa-comments
= merge_request.mr_and_commit_notes.count
= note_count
.merge-request-info
= link_to "##{merge_request.iid}", merge_request_path(merge_request), class: "light"
- if merge_request.assignee
......
......@@ -40,7 +40,7 @@
= link_to merge_request_path(@merge_request) do
%i.fa.fa-comments
Discussion
%span.badge= @merge_request.mr_and_commit_notes.count
%span.badge= @merge_request.mr_and_commit_notes.user.count
%li.commits-tab{data: {action: 'commits'}}
= link_to merge_request_path(@merge_request), title: 'Commits' do
%i.fa.fa-history
......
......@@ -60,11 +60,12 @@
Participants
%span.badge= @users.count
.pull-right
= link_to new_namespace_project_issue_path(@project.namespace, @project, issue: { milestone_id: @milestone.id }), class: "btn btn-grouped", title: "New Issue" do
%i.fa.fa-plus
New Issue
= link_to 'Browse Issues', namespace_project_issues_path(@milestone.project.namespace, @milestone.project, milestone_id: @milestone.id), class: "btn edit-milestone-link btn-grouped"
- if @project.issues_enabled
.pull-right
= link_to new_namespace_project_issue_path(@project.namespace, @project, issue: { milestone_id: @milestone.id }), class: "btn btn-grouped", title: "New Issue" do
%i.fa.fa-plus
New Issue
= link_to 'Browse Issues', namespace_project_issues_path(@milestone.project.namespace, @milestone.project, milestone_id: @milestone.id), class: "btn edit-milestone-link btn-grouped"
.tab-content
.tab-pane.active#tab-issues
......
......@@ -74,9 +74,9 @@
= f.text_field :import_url, class: 'form-control', placeholder: 'https://username:password@gitlab.company.com/group/project.git'
.alert.alert-info.prepend-top-10
%ul
%li
%li
The repository must be accessible over HTTP(S). If it is not publicly accessible, you can add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>.
%li
%li
The import will time out after 4 minutes. For big repositories, use a clone/push combination.
%li
To migrate an SVN repository, check out #{link_to "this document", "http://doc.gitlab.com/ce/workflow/migrating_from_svn.html"}.
......@@ -112,6 +112,6 @@
$ ->
$('.how_to_import_link').bind 'click', (e) ->
e.preventDefault()
import_modal = $(this).parent().find(".modal").show()
import_modal = $(this).next(".modal").show()
$('.modal-header .close').bind 'click', ->
$(".modal").hide()
......@@ -2,7 +2,8 @@
.timeline-entry
.timeline-entry-inner
.timeline-icon
= image_tag avatar_icon(note.author_email), class: "avatar s40"
= link_to user_path(note.author) do
= image_tag avatar_icon(note.author_email), class: "avatar s40"
.timeline-content
- if note.for_merge_request?
- if note.outdated?
......
......@@ -4,7 +4,8 @@
- if note.system
%span.fa.fa-circle
- else
= image_tag avatar_icon(note.author_email), class: "avatar s40"
= link_to user_path(note.author) do
= image_tag avatar_icon(note.author_email), class: "avatar s40"
.timeline-content
.note-header
.note-actions
......@@ -21,7 +22,8 @@
%i.fa.fa-trash-o.cred
Remove
- if note.system
= image_tag avatar_icon(note.author_email), class: "avatar s16"
= link_to user_path(note.author) do
= image_tag avatar_icon(note.author_email), class: "avatar s16"
= link_to_member(@project, note.author, avatar: false)
%span.author-username
= '@' + note.author.username
......
......@@ -2,13 +2,13 @@
- if diff
.diff-file
.diff-header
- if diff.deleted_file
%span= diff.old_path
- else
%span= diff.new_path
- if diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode
%span.file-mode= "#{diff.a_mode}#{diff.b_mode}"
%br/
%span
- if diff.deleted_file
= diff.old_path
- else
= diff.new_path
- if diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode
%span.file-mode= "#{diff.a_mode}#{diff.b_mode}"
.diff-content
%table
- note.truncated_diff_lines.each do |line|
......
%h3.page-title
My Snippets
Your Snippets
.pull-right
= link_to new_snippet_path, class: "btn btn-new btn-grouped", title: "New Snippet" do
Add new snippet
......
......@@ -2,12 +2,12 @@
Public snippets
.pull-right
- if current_user
= link_to new_snippet_path, class: "btn btn-new btn-grouped", title: "New Snippet" do
Add new snippet
= link_to user_snippets_path(current_user), class: "btn btn-grouped" do
My snippets
Your snippets
%p.light
Public snippets created by you and other users are listed here
......
......@@ -23,7 +23,7 @@
.back-link
- if @snippet.author == current_user
= link_to user_snippets_path(current_user) do
&larr; my snippets
&larr; your snippets
- else
= link_to snippets_path do
&larr; discover snippets
......
- if @contributed_projects.present?
.panel.panel-default
.panel.panel-default.contributed-projects
.panel-heading Projects contributed to
= render 'shared/projects_list',
projects: @contributed_projects.sort_by(&:star_count).reverse,
......
%h4 Commits calendar
%h4
Contributions calendar
.pull-right
%small Issues, merge requests and push events
#cal-heatmap.calendar
:javascript
new calendar(
......
.calendar_commit_activity
%hr
%h4
Commit Activity
%strong
- if @commit_count == 0
no
- else
= @commit_count
%span.calendar_commit_date
unique
= 'commit'.pluralize(@commit_count)
on
= @calendar_date.strftime("%b %d, %Y") rescue ''
-unless @commit_count == 0
%hr
- @calendar_activities.each do |project, commits|
- next if commits.empty?
%div.js-toggle-container
%h4.prepend-top-20
%span.light Contributions for
%strong #{@calendar_date.to_s(:short)}
%ul.bordered-list
- @events.sort_by(&:created_at).each do |event|
%li
%span.light
%i.fa.fa-clock-o
= event.created_at.to_s(:time)
- if event.push?
#{event.action_name} #{event.ref_type} #{event.ref_name}
- else
= event_action_name(event)
- if event.target
%strong= link_to "##{event.target_iid}", [event.project.namespace.becomes(Namespace), event.project, event.target]
at
%strong
= pluralize(commits.count, 'commit')
in project
= link_to project.name_with_namespace, project_path(project)
%a.text-expander.js-toggle-button &hellip;
%hr
%div.js-toggle-content
- commits.each do |commit|
%span.monospace
= commit.committed_date.strftime("%H:%M")
= link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id"
= link_to commit.message, namespace_project_commit_path(project.namespace, project, commit), class: "commit-row-message str-truncated"
%br
%hr
- if event.project
= link_to_project event.project
- else
= event.project_name
......@@ -40,7 +40,8 @@
%strong
%i.fa.fa-rss
= render @events
.content_list
= spinner
%aside.col-md-4
= render 'profile', user: @user
= render 'projects'
......
class EmailsOnPushWorker
include Sidekiq::Worker
def perform(project_id, recipients, push_data, send_from_committer_email = false, disable_diffs = false)
def perform(project_id, recipients, push_data, send_from_committer_email: false, disable_diffs: false)
project = Project.find(project_id)
before_sha = push_data["before"]
after_sha = push_data["after"]
branch = push_data["ref"]
ref = push_data["ref"]
author_id = push_data["user_id"]
if Gitlab::Git.blank_ref?(before_sha) || Gitlab::Git.blank_ref?(after_sha)
# skip if new branch was pushed or branch was removed
return true
end
action =
if Gitlab::Git.blank_ref?(before_sha)
:create
elsif Gitlab::Git.blank_ref?(after_sha)
:delete
else
:push
end
compare = Gitlab::Git::Compare.new(project.repository.raw_repository, before_sha, after_sha)
compare = nil
reverse_compare = false
if action == :push
compare = Gitlab::Git::Compare.new(project.repository.raw_repository, before_sha, after_sha)
return false if compare.same
return false if compare.same
if compare.commits.empty?
compare = Gitlab::Git::Compare.new(project.repository.raw_repository, after_sha, before_sha)
if compare.commits.empty?
compare = Gitlab::Git::Compare.new(project.repository.raw_repository, after_sha, before_sha)
reverse_compare = true
reverse_compare = true
return false if compare.commits.empty?
return false if compare.commits.empty?
end
end
recipients.split(" ").each do |recipient|
Notify.repository_push_email(
project_id,
recipient,
author_id,
branch,
compare,
reverse_compare,
send_from_committer_email,
disable_diffs
author_id: author_id,
ref: ref,
action: action,
compare: compare,
reverse_compare: reverse_compare,
send_from_committer_email: send_from_committer_email,
disable_diffs: disable_diffs
).deliver
end
ensure
......
......@@ -323,6 +323,9 @@ production: &base
rack_attack:
git_basic_auth:
# Rack Attack IP banning enabled
# enabled: true
#
# Whitelist requests from 127.0.0.1 for web proxies (NGINX/Apache) with incorrect headers
# ip_whitelist: ["127.0.0.1"]
#
......
......@@ -218,6 +218,7 @@ Settings['extra'] ||= Settingslogic.new({})
#
Settings['rack_attack'] ||= Settingslogic.new({})
Settings.rack_attack['git_basic_auth'] ||= Settingslogic.new({})
Settings.rack_attack.git_basic_auth['enabled'] = true if Settings.rack_attack.git_basic_auth['enabled'].nil?
Settings.rack_attack.git_basic_auth['ip_whitelist'] ||= %w{127.0.0.1}
Settings.rack_attack.git_basic_auth['maxretry'] ||= 10
Settings.rack_attack.git_basic_auth['findtime'] ||= 1.minute
......
class SetIncorrectAssigneeIdToNull < ActiveRecord::Migration
def up
execute "UPDATE issues SET assignee_id = NULL WHERE assignee_id = -1"
execute "UPDATE merge_requests SET assignee_id = NULL WHERE assignee_id = -1"
end
end
......@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20150320234437) do
ActiveRecord::Schema.define(version: 20150324155957) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
......
......@@ -46,14 +46,15 @@ You can also use other rich text files in GitLab. You might have to install a de
GFM honors the markdown specification in how [paragraphs and line breaks are handled](http://daringfireball.net/projects/markdown/syntax#p).
A paragraph is simply one or more consecutive lines of text, separated by one or more blank lines.:
A paragraph is simply one or more consecutive lines of text, separated by one or more blank lines.
Line-breaks, or softreturns, are rendered if you end a line with two or more spaces
Roses are red
Roses are red [followed by two or more spaces]
Violets are blue
Sugar is sweet
Roses are red
Roses are red
Violets are blue
Sugar is sweet
......
......@@ -24,7 +24,7 @@ If you have local changes to your GitLab repository the script will stash them a
## 2. Run GitLab upgrade tool
Note: GitLab 7.9 adds nodejs as a dependency. GitLab 7.6 adds `libkrb5-dev` as a dependency (installed by default on Ubuntu and OSX). GitLab 7.2 adds `pkg-config` and `cmake` as dependency. Please check the dependencies in the [installation guide.](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md#1-packages-dependencies)
Note: GitLab 7.9 adds `nodejs` as a dependency. GitLab 7.6 adds `libkrb5-dev` as a dependency (installed by default on Ubuntu and OSX). GitLab 7.2 adds `pkg-config` and `cmake` as dependency. Please check the dependencies in the [installation guide.](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md#1-packages-dependencies)
# Starting with GitLab version 7.0 upgrader script has been moved to bin directory
cd /home/git/gitlab
......
......@@ -11,7 +11,7 @@ RUN apt-get update -q \
# If the Omnibus package version below is outdated please contribute a merge request to update it.
# If you run GitLab Enterprise Edition point it to a location where you have downloaded it.
RUN TMP_FILE=$(mktemp); \
wget -q -O $TMP_FILE https://downloads-packages.s3.amazonaws.com/ubuntu-14.04/gitlab_7.8.3-omnibus-1_amd64.deb \
wget -q -O $TMP_FILE https://downloads-packages.s3.amazonaws.com/ubuntu-14.04/gitlab_7.9.0-omnibus.2-1_amd64.deb \
&& dpkg -i $TMP_FILE \
&& rm -f $TMP_FILE
......
......@@ -15,3 +15,10 @@ Feature: Admin Settings
Then I should see the help text
And I logout
Then I should see the help text
Scenario: Change Slack Service Template settings
When I click on "Service Templates"
And I click on "Slack" service
Then I check all events and submit form
And I should see service template settings saved
And I should see all checkboxes checked
@dashboard
Feature: New Project
Background:
Given I sign in as a user
And I own project "Shop"
And I visit dashboard page
@javascript
Scenario: I should see New projects page
Given I click "New project" link
Then I see "New project" page
When I click on "Import project from GitHub"
Then I see instructions on how to import from GitHub
......@@ -29,6 +29,35 @@ class Spinach::Features::AdminSettings < Spinach::FeatureSteps
visit '/help'
end
step 'I click on "Service Templates"' do
click_link 'Service Templates'
end
step 'I click on "Slack" service' do
click_link 'Slack'
end
step 'I check all events and submit form' do
page.check('Active')
page.check('Push events')
page.check('Tag push events')
page.check('Comments')
page.check('Issues events')
page.check('Merge Request events')
fill_in 'Webhook', with: "http://localhost"
click_on 'Save'
end
step 'I should see service template settings saved' do
page.should have_content 'Application settings saved successfully'
end
step 'I should see all checkboxes checked' do
all('input[type=checkbox]').each do |checkbox|
checkbox.should be_checked
end
end
def help_text
'For help related to GitLab contact Marc Smith at marc@smith.example or find him in office 42.'
end
......
class Spinach::Features::NewProject < Spinach::FeatureSteps
include SharedAuthentication
include SharedPaths
include SharedProject
step 'I click "New project" link' do
click_link "New project"
end
step 'I see "New project" page' do
page.should have_content("Project path")
end
step 'I click on "Import project from GitHub"' do
first('.how_to_import_link').click
end
step 'I see instructions on how to import from GitHub' do
github_modal = first('.modal-body')
github_modal.should be_visible
github_modal.should have_content "To enable importing projects from GitHub"
all('.modal-body').each do |element|
element.should_not be_visible unless element == github_modal
end
end
end
......@@ -24,4 +24,37 @@ class Spinach::Features::User < Spinach::FeatureSteps
step 'I should be unsubscribed' do
current_path.should == root_path
end
step '"John Doe" has contributions' do
user = User.find_by(name: 'John Doe')
project = contributed_project
# Issue controbution
issue_params = { title: 'Bug in old browser' }
Issues::CreateService.new(project, user, issue_params).execute
# Push code contribution
push_params = {
project: project,
action: Event::PUSHED,
author_id: user.id,
data: { commit_count: 3 }
}
Event.create(push_params)
end
step 'I should see contributed projects' do
within '.contributed-projects' do
page.should have_content(@contributed_project.name)
end
end
step 'I should see contributions calendar' do
page.should have_css('.cal-heatmap-container')
end
def contributed_project
@contributed_project ||= create(:project, :public)
end
end
......@@ -74,3 +74,12 @@ Feature: User
Then I should see unsubscribe text and button
And I press the unsubscribe button
Then I should be unsubscribed
@javascript
Scenario: "John Doe" contribution profile
Given I sign in as a user
And "John Doe" has contributions
When I visit user "John Doe" page
Then I should see user "John Doe" page
And I should see contributed projects
And I should see contributions calendar
require 'mime/types'
require 'uri'
module API
# Projects API
......@@ -103,7 +104,7 @@ module API
delete ":id/repository/branches/:branch" do
authorize_push_project
result = DeleteBranchService.new(user_project, current_user).
execute(params[:branch])
execute(URI.unescape(params[:branch]))
if result[:status] == :success
{
......
......@@ -20,7 +20,7 @@ module API
identifier = sudo_identifier()
# If the sudo is the current user do nothing
if (identifier && !(@current_user.id == identifier || @current_user.username == identifier))
if identifier && !(@current_user.id == identifier || @current_user.username == identifier)
render_api_error!('403 Forbidden: Must be admin to use sudo', 403) unless @current_user.is_admin?
@current_user = User.by_username_or_id(identifier)
not_found!("No user id or username for: #{identifier}") if @current_user.nil?
......@@ -33,7 +33,7 @@ module API
identifier ||= params[SUDO_PARAM] ||= env[SUDO_HEADER]
# Regex for integers
if (!!(identifier =~ /^[0-9]+$/))
if !!(identifier =~ /^[0-9]+$/)
identifier.to_i
else
identifier
......
......@@ -17,42 +17,40 @@ module API
post "/allowed" do
status 200
actor = if params[:key_id]
Key.find_by(id: params[:key_id])
elsif params[:user_id]
User.find_by(id: params[:user_id])
end
actor =
if params[:key_id]
Key.find_by(id: params[:key_id])
elsif params[:user_id]
User.find_by(id: params[:user_id])
end
unless actor
return Gitlab::GitAccessStatus.new(false, 'No such user or key')
end
project_path = params[:project]
# Check for *.wiki repositories.
# Strip out the .wiki from the pathname before finding the
# project. This applies the correct project permissions to
# the wiki repository as well.
access =
if project_path.end_with?('.wiki')
project_path.chomp!('.wiki')
Gitlab::GitAccessWiki.new
else
Gitlab::GitAccess.new
end
wiki = project_path.end_with?('.wiki')
project_path.chomp!('.wiki') if wiki
project = Project.find_with_namespace(project_path)
if project
status = access.check(
actor,
params[:action],
project,
params[:changes]
)
access =
if wiki
Gitlab::GitAccessWiki.new(actor, project)
else
Gitlab::GitAccess.new(actor, project)
end
status = access.check(params[:action], params[:changes])
end
if project && status && status.allowed?
if project && access.can_read_project?
status
else
Gitlab::GitAccessStatus.new(false, 'No such project')
......
......@@ -178,7 +178,8 @@ module API
put ":id/merge_request/:merge_request_id/merge" do
merge_request = user_project.merge_requests.find(params[:merge_request_id])
allowed = ::Gitlab::GitAccess.can_push_to_branch?(current_user, user_project, merge_request.target_branch)
allowed = ::Gitlab::GitAccess.new(current_user, user_project).
can_push_to_branch?(merge_request.target_branch)
if allowed
if merge_request.unchecked?
......
require_relative 'rack_attack_helpers'
require_relative 'shell_env'
module Grack
......@@ -85,25 +86,41 @@ module Grack
user = oauth_access_token_check(login, password)
end
return user if user.present?
# At this point, we know the credentials were wrong. We let Rack::Attack
# know there was a failed authentication attempt from this IP. This
# information is stored in the Rails cache (Redis) and will be used by
# the Rack::Attack middleware to decide whether to block requests from
# this IP.
# If the user authenticated successfully, we reset the auth failure count
# from Rack::Attack for that IP. A client may attempt to authenticate
# with a username and blank password first, and only after it receives
# a 401 error does it present a password. Resetting the count prevents
# false positives from occurring.
#
# Otherwise, we let Rack::Attack know there was a failed authentication
# attempt from this IP. This information is stored in the Rails cache
# (Redis) and will be used by the Rack::Attack middleware to decide
# whether to block requests from this IP.
config = Gitlab.config.rack_attack.git_basic_auth
Rack::Attack::Allow2Ban.filter(@request.ip, config) do
# Unless the IP is whitelisted, return true so that Allow2Ban
# increments the counter (stored in Rails.cache) for the IP
if config.ip_whitelist.include?(@request.ip)
false
if config.enabled
if user
# A successful login will reset the auth failure count from this IP
Rack::Attack::Allow2Ban.reset(@request.ip, config)
else
true
banned = Rack::Attack::Allow2Ban.filter(@request.ip, config) do
# Unless the IP is whitelisted, return true so that Allow2Ban
# increments the counter (stored in Rails.cache) for the IP
if config.ip_whitelist.include?(@request.ip)
false
else
true
end
end
if banned
Rails.logger.info "IP #{@request.ip} failed to login " \
"as #{login} but has been temporarily banned from Git auth"
end
end
end
nil # No user was found
user
end
def authorized_request?
......@@ -112,7 +129,7 @@ module Grack
case git_cmd
when *Gitlab::GitAccess::DOWNLOAD_COMMANDS
if user
Gitlab::GitAccess.new.download_access_check(user, project).allowed?
Gitlab::GitAccess.new(user, project).download_access_check.allowed?
elsif project.public?
# Allow clone/fetch for public projects
true
......
# rack-attack v4.2.0 doesn't yet support clearing of keys.
# Taken from https://github.com/kickstarter/rack-attack/issues/113
class Rack::Attack::Allow2Ban
def self.reset(discriminator, options)
findtime = options[:findtime] or raise ArgumentError, "Must pass findtime option"
cache.reset_count("#{key_prefix}:count:#{discriminator}", findtime)
cache.delete("#{key_prefix}:ban:#{discriminator}")
end
end
class Rack::Attack::Cache
def reset_count(unprefixed_key, period)
epoch_time = Time.now.to_i
# Add 1 to expires_in to avoid timing error: http://git.io/i1PHXA
expires_in = period - (epoch_time % period) + 1
key = "#{(epoch_time / period).to_i}:#{unprefixed_key}"
delete(key)
end
def delete(unprefixed_key)
store.delete("#{prefix}:#{unprefixed_key}")
end
end
class Rack::Attack::StoreProxy::RedisStoreProxy
def delete(key, options={})
self.del(key)
rescue Redis::BaseError
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.
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