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