Merge branch 'upstream-ce' into 'master'

Pull 7.1.0.pre from Community Edition

See merge request !126
This commit is contained in:
Dmitriy Zaporozhets
2014-07-11 14:17:00 +00:00
231 changed files with 1679 additions and 722 deletions
+13
View File
@@ -7,6 +7,19 @@ v 7.1.0
- Add @all mention for comments
- Dont show reply button if user is not signed in
- Expose more information for issues with webhook
- Add a mention of the merge request into the default merge request commit message
- Imrpove code highlight, introduce support for more languages like Go, Clojure, Erlang etc
- Fix concurrency issue in repository download
- Dont allow repository name start with ?
- Improve email threading (Pierre de La Morinerie)
- Cleaner help page
- Group milestones
- Improved email notifications
- Contributors API (sponsored by Mobbr)
- Fix LDAP TLS authentication (Boris HUISGEN)
- Show VERSION information on project sidebar
- Improve branch removal logic when accept MR
- Fix bug where comment form is spawned inside the Reply button
v 7.0.0
- The CPU no longer overheats when you hold down the spacebar
+1 -1
View File
@@ -42,7 +42,7 @@ Please send a merge request with a tested solution or a merge request with a fai
1. **Observed behavior**
1. **Relevant logs and/or screenshots:** Please use code blocks (\`\`\`) to format console output, logs, and code as it's very hard to read otherwise.
1. **Output of checks**
* Results of GitLab [Application Check](doc/install/installation.md#check-application-status) (`sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production`); we will only investigate if the tests are passing
* Results of GitLab [Application Check](doc/install/installation.md#check-application-status) (`sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production SANITIZE=true`); we will only investigate if the tests are passing
* Version of GitLab you are running; we will only investigate issues in the latest stable and development releases as per the [maintenance policy](MAINTENANCE.md)
* Add the last commit sha1 of the GitLab version you used to replicate the issue (obtainable from the help page)
* Describe your setup (use relevant parts from `sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production`)
+2 -5
View File
@@ -10,8 +10,6 @@ end
gem "rails", "~> 4.1.0"
gem "protected_attributes"
# Make links from text
gem 'rails_autolink', '~> 1.1'
@@ -23,8 +21,8 @@ gem "mysql2", group: :mysql
gem "pg", group: :postgres
# Auth
gem "devise", '3.0.4'
gem "devise-async", '0.8.0'
gem "devise", '3.2.4'
gem "devise-async", '0.9.0'
gem 'omniauth', "~> 1.1.3"
gem 'omniauth-google-oauth2'
gem 'omniauth-twitter'
@@ -49,7 +47,6 @@ gem "gitlab-linguist", "~> 3.0.0", require: "linguist"
# API
gem "grape", "~> 0.6.1"
# Replace with rubygems when nesteted entities get released
gem "grape-entity", "~> 0.4.2"
gem 'rack-cors', require: 'rack/cors'
+10 -12
View File
@@ -40,7 +40,7 @@ GEM
axiom-types (0.0.5)
descendants_tracker (~> 0.0.1)
ice_nine (~> 0.9)
bcrypt-ruby (3.1.2)
bcrypt (3.1.7)
better_errors (1.0.1)
coderay (>= 1.0.0)
erubis (>= 2.6.6)
@@ -94,13 +94,14 @@ GEM
default_value_for (3.0.0)
activerecord (>= 3.2.0, < 5.0)
descendants_tracker (0.0.3)
devise (3.0.4)
bcrypt-ruby (~> 3.0)
devise (3.2.4)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 3.2.6, < 5)
thread_safe (~> 0.1)
warden (~> 1.2.3)
devise-async (0.8.0)
devise (>= 2.2, < 3.2)
devise-async (0.9.0)
devise (~> 3.2)
diff-lcs (1.2.5)
diffy (3.0.3)
docile (1.1.1)
@@ -175,7 +176,7 @@ GEM
mime-types (~> 1.19)
gitlab_emoji (0.0.1.1)
emoji (~> 1.0.1)
gitlab_git (6.0.0)
gitlab_git (6.0.1)
activesupport (~> 4.0)
charlock_holmes (~> 0.6)
gitlab-grit (~> 2.6)
@@ -282,7 +283,7 @@ GEM
method_source (0.8.2)
mime-types (1.25.1)
mini_portile (0.6.0)
minitest (5.3.4)
minitest (5.3.5)
multi_json (1.10.1)
multi_xml (0.5.5)
multipart-post (1.2.0)
@@ -331,8 +332,6 @@ GEM
websocket-driver (>= 0.2.0)
polyglot (0.3.4)
posix-spawn (0.3.8)
protected_attributes (1.0.5)
activemodel (>= 4.0.1, < 5.0)
pry (0.9.12.4)
coderay (~> 1.0)
method_source (~> 0.8)
@@ -586,8 +585,8 @@ DEPENDENCIES
d3_rails (~> 3.1.4)
database_cleaner
default_value_for (~> 3.0.0)
devise (= 3.0.4)
devise-async (= 0.8.0)
devise (= 3.2.4)
devise-async (= 0.9.0)
diffy (~> 3.0.3)
dropzonejs-rails
email_spec
@@ -636,7 +635,6 @@ DEPENDENCIES
org-ruby
pg
poltergeist (~> 1.5.1)
protected_attributes
pry
quiet_assets (~> 1.0.1)
rack-attack
+1 -1
View File
@@ -23,7 +23,7 @@
#= require g.raphael-min
#= require g.bar-min
#= require branch-graph
#= require highlightjs.min
#= require highlight.pack
#= require ace/ace
#= require d3
#= require underscore
+1 -1
View File
@@ -4,4 +4,4 @@ Array.prototype.first = function() {
Array.prototype.last = function() {
return this[this.length-1];
}
}
+1 -1
View File
@@ -14,4 +14,4 @@ $ ->
$('.js-group-avatar-input').bind "change", ->
form = $(this).closest("form")
filename = $(this).val().replace(/^.*[\\\/]/, '')
form.find(".js-avatar-filename").text(filename)
form.find(".js-avatar-filename").text(filename)
@@ -182,4 +182,4 @@ $(document).ready ->
$(".div-dropzone").click()
return
return
return
+1 -1
View File
@@ -376,7 +376,7 @@ class Notes
###
replyToDiscussionNote: (e) =>
form = $(".js-new-note-form")
replyLink = $(e.target)
replyLink = $(e.target).closest(".js-discussion-reply-button")
replyLink.hide()
# insert the form after the button
+1 -1
View File
@@ -27,4 +27,4 @@ $ ->
filename = $(this).val().replace(/^.*[\\\/]/, '')
form.find(".js-avatar-filename").text(filename)
$('.profile-groups-avatars').tooltip("placement": "top")
$('.profile-groups-avatars').tooltip("placement": "top")
+4 -4
View File
@@ -152,16 +152,16 @@
}
&.btn-close {
color: #B94A48;
font-weight: bold;
color: $bg_danger;
border-color: $border_danger;
&:hover {
color: #B94A48;
}
}
&.btn-reopen {
color: #468847;
font-weight: bold;
color: $bg_success;
border-color: $border_success;
&:hover {
color: #468847;
}
+10 -2
View File
@@ -177,10 +177,18 @@ li.commit {
.commit-row-description {
font-size: 14px;
border-left: 1px solid #e5e5e5;
padding: 0 15px 0 7px;
border-left: 1px solid #EEE;
padding: 10px 15px;
margin: 5px 0 10px 5px;
background: #f9f9f9;
display: none;
pre {
border: none;
background: inherit;
padding: 0;
margin: 0;
}
}
.commit-row-info {
@@ -7,3 +7,7 @@
.member-search-form {
float: left;
}
.milestone-row {
@include str-truncated(90%);
}
+5 -8
View File
@@ -53,13 +53,9 @@ header {
font-size: 18px;
.app_logo { margin-left: -15px; }
.title {
display: inline-block;
overflow: hidden;
text-overflow: ellipsis;
vertical-align: top;
white-space: nowrap;
max-width: 70%;
@include str-truncated(70%);
}
.navbar-collapse {
@@ -130,6 +126,7 @@ header {
margin: 0;
margin-left: 5px;
@include header-font;
@include str-truncated(37%);
}
.profile-pic {
@@ -254,7 +251,7 @@ header {
.search .search-input {
width: 300px;
&:focus {
width: 400px;
width: 330px;
}
}
@@ -262,7 +259,7 @@ header {
.search .search-input {
width: 200px;
&:focus {
width: 300px;
width: 230px;
}
}
}
@@ -28,6 +28,7 @@
}
td {
border-color: #F1F1F1 !important;
border-bottom: 1px solid;
}
&:hover {
td {
@@ -6,7 +6,7 @@ class Admin::BroadcastMessagesController < Admin::ApplicationController
end
def create
@broadcast_message = BroadcastMessage.new(params[:broadcast_message])
@broadcast_message = BroadcastMessage.new(broadcast_message_params)
if @broadcast_message.save
redirect_to admin_broadcast_messages_path, notice: 'Broadcast Message was successfully created.'
@@ -29,4 +29,11 @@ class Admin::BroadcastMessagesController < Admin::ApplicationController
def broadcast_messages
@broadcast_messages ||= BroadcastMessage.order("starts_at DESC").page(params[:page])
end
def broadcast_message_params
params.require(:broadcast_message).permit(
:alert_type, :color, :ends_at, :font,
:message, :starts_at
)
end
end
+6 -2
View File
@@ -20,7 +20,7 @@ class Admin::GroupsController < Admin::ApplicationController
end
def create
@group = Group.new(params[:group])
@group = Group.new(group_params)
@group.path = @group.name.dup.parameterize if @group.name
if @group.save
@@ -32,7 +32,7 @@ class Admin::GroupsController < Admin::ApplicationController
end
def update
if @group.update_attributes(params[:group])
if @group.update_attributes(group_params)
redirect_to [:admin, @group], notice: 'Group was successfully updated.'
else
render "edit"
@@ -56,4 +56,8 @@ class Admin::GroupsController < Admin::ApplicationController
def group
@group = Group.find_by(path: params[:id])
end
def group_params
params.require(:group).permit(:name, :description, :path, :avatar)
end
end
+5 -1
View File
@@ -5,7 +5,7 @@ class Admin::HooksController < Admin::ApplicationController
end
def create
@hook = SystemHook.new(params[:hook])
@hook = SystemHook.new(hook_params)
if @hook.save
redirect_to admin_hooks_path, notice: 'Hook was successfully created.'
@@ -37,4 +37,8 @@ class Admin::HooksController < Admin::ApplicationController
redirect_to :back
end
def hook_params
params.require(:hook).permit(:url)
end
end
+17 -15
View File
@@ -13,7 +13,7 @@ class Admin::UsersController < Admin::ApplicationController
end
def new
@user = User.build_user
@user = User.new
end
def edit
@@ -37,15 +37,12 @@ class Admin::UsersController < Admin::ApplicationController
end
def create
admin = params[:user].delete("admin")
opts = {
force_random_password: true,
password_expires_at: Time.now
}
@user = User.build_user(params[:user].merge(opts), as: :admin)
@user.admin = (admin && admin.to_i > 0)
@user = User.new(user_params.merge(opts))
@user.created_by_id = current_user.id
@user.generate_password
@user.skip_confirmation!
@@ -62,19 +59,15 @@ class Admin::UsersController < Admin::ApplicationController
end
def update
admin = params[:user].delete("admin")
if params[:user][:password].blank?
params[:user].delete(:password)
params[:user].delete(:password_confirmation)
end
if admin.present?
user.admin = !admin.to_i.zero?
if params[:user][:password].present?
user_params.merge(
password: params[:user][:password],
password_confirmation: params[:user][:password_confirmation],
)
end
respond_to do |format|
if user.update_attributes(params[:user], as: :admin)
if user.update_attributes(user_params)
user.confirm!
format.html { redirect_to [:admin, user], notice: 'User was successfully updated.' }
format.json { head :ok }
@@ -115,4 +108,13 @@ class Admin::UsersController < Admin::ApplicationController
def user
@user ||= User.find_by!(username: params[:id])
end
def user_params
params.require(:user).permit(
:email, :remember_me, :bio, :name, :username,
:skype, :linkedin, :twitter, :website_url, :color_scheme_id, :theme_id, :force_random_password,
:extern_uid, :provider, :password_expires_at, :avatar, :hide_no_ssh_key,
:projects_limit, :can_create_group, :admin
)
end
end
+21 -2
View File
@@ -1,6 +1,7 @@
require 'gon'
class ApplicationController < ActionController::Base
before_filter :authenticate_user_from_token!
before_filter :authenticate_user!
before_filter :reject_blocked!
before_filter :check_password_expiration
@@ -28,6 +29,25 @@ class ApplicationController < ActionController::Base
protected
# From https://github.com/plataformatec/devise/wiki/How-To:-Simple-Token-Authentication-Example
# https://gist.github.com/josevalim/fb706b1e933ef01e4fb6
def authenticate_user_from_token!
user_token = if params[:authenticity_token].presence
params[:authenticity_token].presence
elsif params[:private_token].presence
params[:private_token].presence
end
user = user_token && User.find_by_authentication_token(user_token.to_s)
if user
# Notice we are passing store false, so the user is not
# actually stored in the session and a token is needed
# for every request. If you want the token to work as a
# sign in token, you can simply remove store: false.
sign_in user, store: false
end
end
def log_exception(exception)
application_trace = ActionDispatch::ExceptionWrapper.new(env, exception).application_trace
application_trace.map!{ |t| " #{t}\n" }
@@ -227,8 +247,7 @@ class ApplicationController < ActionController::Base
end
def configure_permitted_parameters
devise_parameter_sanitizer.for(:sign_in) { |u| u.permit(:username, :email, :password, :login, :remember_me) }
devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:username, :email, :name, :password, :password_confirmation) }
devise_parameter_sanitizer.sanitize(:sign_in) { |u| u.permit(:username, :email, :password, :login, :remember_me) }
end
def hexdigest(string)
@@ -0,0 +1,56 @@
class Groups::MilestonesController < ApplicationController
layout 'group'
before_filter :authorize_group_milestone!, only: :update
def index
project_milestones = case params[:status]
when 'all'; status
when 'closed'; status('closed')
else status('active')
end
@group_milestones = Milestones::GroupService.new(project_milestones).execute
@group_milestones = Kaminari.paginate_array(@group_milestones).page(params[:page]).per(30)
end
def show
project_milestones = Milestone.where(project_id: group.projects).order("due_date ASC")
@group_milestone = Milestones::GroupService.new(project_milestones).milestone(title)
end
def update
project_milestones = Milestone.where(project_id: group.projects).order("due_date ASC")
@group_milestones = Milestones::GroupService.new(project_milestones).milestone(title)
@group_milestones.milestones.each do |milestone|
Milestones::UpdateService.new(milestone.project, current_user, params[:milestone]).execute(milestone)
end
respond_to do |format|
format.js
format.html do
redirect_to group_milestones_path(group)
end
end
end
private
def group
@group ||= Group.find_by(path: params[:group_id])
end
def title
params[:title]
end
def status(state = nil)
conditions = { project_id: group.projects }
conditions.reverse_merge!(state: state) if state
Milestone.where(conditions).order("title ASC")
end
def authorize_group_milestone!
return render_404 unless can?(current_user, :manage_group, group)
end
end
+6 -2
View File
@@ -22,7 +22,7 @@ class GroupsController < ApplicationController
end
def create
@group = Group.new(params[:group])
@group = Group.new(group_params)
@group.path = @group.name.dup.parameterize if @group.name
if @group.save
@@ -86,7 +86,7 @@ class GroupsController < ApplicationController
end
def update
if @group.update_attributes(params[:group])
if @group.update_attributes(group_params)
redirect_to edit_group_path(@group), notice: 'Group was successfully updated.'
else
render action: "edit"
@@ -161,4 +161,8 @@ class GroupsController < ApplicationController
params[:state] = 'opened' if params[:state].blank?
params[:group_id] = @group.id
end
def group_params
params.require(:group).permit(:name, :description, :path, :avatar, :ldap_access, :ldap_cn)
end
end
@@ -7,7 +7,7 @@ class Profiles::EmailsController < ApplicationController
end
def create
@email = current_user.emails.new(params[:email])
@email = current_user.emails.new(email_params)
flash[:alert] = @email.errors.full_messages.first unless @email.save
@@ -23,4 +23,10 @@ class Profiles::EmailsController < ApplicationController
format.js { render nothing: true }
end
end
private
def email_params
params.require(:email).permit(:email)
end
end
+6 -1
View File
@@ -15,7 +15,7 @@ class Profiles::KeysController < ApplicationController
end
def create
@key = current_user.keys.new(params[:key])
@key = current_user.keys.new(key_params)
if @key.save
redirect_to profile_key_path(@key)
@@ -53,4 +53,9 @@ class Profiles::KeysController < ApplicationController
end
end
private
def key_params
params.require(:key).permit(:title, :key)
end
end
@@ -11,8 +11,8 @@ class Profiles::PasswordsController < ApplicationController
end
def create
new_password = params[:user][:password]
new_password_confirmation = params[:user][:password_confirmation]
new_password = user_params[:password]
new_password_confirmation = user_params[:password_confirmation]
result = @user.update_attributes(
password: new_password,
@@ -31,11 +31,11 @@ class Profiles::PasswordsController < ApplicationController
end
def update
password_attributes = params[:user].select do |key, value|
password_attributes = user_params.select do |key, value|
%w(password password_confirmation).include?(key.to_s)
end
unless @user.valid_password?(params[:user][:current_password])
unless @user.valid_password?(user_params[:current_password])
redirect_to edit_profile_password_path, alert: 'You must provide a valid current password'
return
end
@@ -74,4 +74,8 @@ class Profiles::PasswordsController < ApplicationController
def authorize_change_password!
return render_404 if @user.ldap_user?
end
def user_params
params.require(:user).permit(:current_password, :password, :password_confirmation)
end
end
+11 -3
View File
@@ -14,9 +14,9 @@ class ProfilesController < ApplicationController
end
def update
params[:user].delete(:email) if @user.ldap_user?
user_params.except!(:email) if @user.ldap_user?
if @user.update_attributes(params[:user])
if @user.update_attributes(user_params)
flash[:notice] = "Profile was successfully updated"
else
flash[:alert] = "Failed to update profile"
@@ -41,7 +41,7 @@ class ProfilesController < ApplicationController
end
def update_username
@user.update_attributes(username: params[:user][:username])
@user.update_attributes(username: user_params[:username])
respond_to do |format|
format.js
@@ -57,4 +57,12 @@ class ProfilesController < ApplicationController
def authorize_change_username!
return render_404 unless @user.can_change_username?
end
def user_params
params.require(:user).permit(
:email, :password, :password_confirmation, :bio, :name, :username,
:skype, :linkedin, :twitter, :website_url, :color_scheme_id, :theme_id,
:avatar, :hide_no_ssh_key,
)
end
end
+7 -3
View File
@@ -30,8 +30,12 @@ class Projects::BlobController < Projects::ApplicationController
def blob
@blob ||= @repository.blob_at(@commit.id, @path)
return not_found! unless @blob
@blob
if @blob
@blob
elsif tree.entries.any?
redirect_to project_tree_path(@project, File.join(@ref, @path)) and return
else
return not_found!
end
end
end
@@ -22,7 +22,7 @@ class Projects::DeployKeysController < Projects::ApplicationController
end
def create
@key = DeployKey.new(params[:deploy_key])
@key = DeployKey.new(deploy_key_params)
if @key.valid? && @project.deploy_keys << @key
redirect_to project_deploy_keys_path(@project)
@@ -58,4 +58,8 @@ class Projects::DeployKeysController < Projects::ApplicationController
def available_keys
@available_keys ||= current_user.accessible_deploy_keys
end
def deploy_key_params
params.require(:deploy_key).permit(:key, :title)
end
end
@@ -28,8 +28,6 @@ class Projects::EditTreeController < Projects::BaseTreeController
def preview
@content = params[:content]
#FIXME workaround https://github.com/gitlabhq/gitlabhq/issues/5936
@content += "\n" if @blob.data.end_with?("\n")
diffy = Diffy::Diff.new(@blob.data, @content, diff: '-U 3',
include_diff_info: true)
@@ -14,7 +14,7 @@ class Projects::GitHooksController < Projects::ApplicationController
def update
@pre_receive_hook = project.git_hook
@pre_receive_hook.update_attributes(params[:git_hook])
@pre_receive_hook.update_attributes(git_hook_params)
if @pre_receive_hook.valid?
redirect_to project_git_hooks_path(@project)
@@ -22,4 +22,11 @@ class Projects::GitHooksController < Projects::ApplicationController
render :index
end
end
private
# Only allow a trusted parameter "white list" through.
def git_hook_params
params.require(:git_hook).permit(:deny_delete_tag, :delete_branch_regex, :commit_message_regex, :force_push_regex)
end
end
+5 -1
View File
@@ -12,7 +12,7 @@ class Projects::HooksController < Projects::ApplicationController
end
def create
@hook = @project.hooks.new(params[:hook])
@hook = @project.hooks.new(hook_params)
@hook.save
if @hook.valid?
@@ -40,4 +40,8 @@ class Projects::HooksController < Projects::ApplicationController
def hook
@hook ||= @project.hooks.find(params[:id])
end
def hook_params
params.require(:hook).permit(:url, :push_events, :issues_events, :merge_requests_events, :tag_push_events)
end
end
+14 -3
View File
@@ -42,7 +42,11 @@ class Projects::IssuesController < Projects::ApplicationController
end
def new
@issue = @project.issues.new(params[:issue])
params[:issue] ||= ActionController::Parameters.new(
assignee_id: ""
)
@issue = @project.issues.new(issue_params)
respond_with(@issue)
end
@@ -59,7 +63,7 @@ class Projects::IssuesController < Projects::ApplicationController
end
def create
@issue = Issues::CreateService.new(project, current_user, params[:issue]).execute
@issue = Issues::CreateService.new(project, current_user, issue_params).execute
respond_to do |format|
format.html do
@@ -76,7 +80,7 @@ class Projects::IssuesController < Projects::ApplicationController
end
def update
@issue = Issues::UpdateService.new(project, current_user, params[:issue]).execute(issue)
@issue = Issues::UpdateService.new(project, current_user, issue_params).execute(issue)
respond_to do |format|
format.js
@@ -144,4 +148,11 @@ class Projects::IssuesController < Projects::ApplicationController
raise ActiveRecord::RecordNotFound.new
end
end
def issue_params
params.require(:issue).permit(
:title, :assignee_id, :position, :description,
:milestone_id, :label_list, :state_event
)
end
end
@@ -60,7 +60,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
def new
@merge_request = MergeRequest.new(params[:merge_request])
params[:merge_request] ||= ActionController::Parameters.new(
source_project: @project
)
@merge_request = MergeRequest.new(merge_request_params)
@merge_request.source_project = @project unless @merge_request.source_project
@merge_request.target_project ||= (@project.forked_from_project || @project)
@target_branches = @merge_request.target_project.nil? ? [] : @merge_request.target_project.repository.branch_names
@@ -111,7 +115,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def create
@target_branches ||= []
@merge_request = MergeRequests::CreateService.new(project, current_user, params[:merge_request]).execute
@merge_request = MergeRequests::CreateService.new(project, current_user, merge_request_params).execute
if @merge_request.valid?
redirect_to project_merge_request_path(@merge_request.target_project, @merge_request), notice: 'Merge request was successfully created.'
@@ -123,7 +127,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
def update
@merge_request = MergeRequests::UpdateService.new(project, current_user, params[:merge_request]).execute(@merge_request)
@merge_request = MergeRequests::UpdateService.new(project, current_user, merge_request_params).execute(@merge_request)
if @merge_request.valid?
respond_to do |format|
@@ -264,4 +268,12 @@ class Projects::MergeRequestsController < Projects::ApplicationController
can?(current_user, action, project)
end
def merge_request_params
params.require(:merge_request).permit(
:title, :assignee_id, :source_project_id, :source_branch,
:target_project_id, :target_branch, :milestone_id,
:state_event, :description, :label_list
)
end
end
@@ -37,7 +37,7 @@ class Projects::MilestonesController < Projects::ApplicationController
end
def create
@milestone = Milestones::CreateService.new(project, current_user, params[:milestone]).execute
@milestone = Milestones::CreateService.new(project, current_user, milestone_params).execute
if @milestone.save
redirect_to project_milestone_path(@project, @milestone)
@@ -47,7 +47,7 @@ class Projects::MilestonesController < Projects::ApplicationController
end
def update
@milestone = Milestones::UpdateService.new(project, current_user, params[:milestone]).execute(milestone)
@milestone = Milestones::UpdateService.new(project, current_user, milestone_params).execute(milestone)
respond_to do |format|
format.js
@@ -105,4 +105,8 @@ class Projects::MilestonesController < Projects::ApplicationController
def module_enabled
return render_404 unless @project.issues_enabled
end
def milestone_params
params.require(:milestone).permit(:title, :description, :due_date, :state_event)
end
end
+9 -2
View File
@@ -21,7 +21,7 @@ class Projects::NotesController < Projects::ApplicationController
end
def create
@note = Notes::CreateService.new(project, current_user, params[:note]).execute
@note = Notes::CreateService.new(project, current_user, note_params).execute
respond_to do |format|
format.json { render_note_json(@note) }
@@ -30,7 +30,7 @@ class Projects::NotesController < Projects::ApplicationController
end
def update
note.update_attributes(params[:note])
note.update_attributes(note_params)
note.reset_events_cache
respond_to do |format|
@@ -109,4 +109,11 @@ class Projects::NotesController < Projects::ApplicationController
def authorize_admin_note!
return access_denied! unless can?(current_user, :admin_note, note)
end
def note_params
params.require(:note).permit(
:note, :noteable, :noteable_id, :noteable_type, :project_id,
:attachment, :line_code, :commit_id
)
end
end
@@ -11,7 +11,7 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController
end
def create
@project.protected_branches.create(params[:protected_branch])
@project.protected_branches.create(protected_branch_params)
redirect_to project_protected_branches_path(@project)
end
@@ -23,4 +23,10 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController
format.js { render nothing: true }
end
end
private
def protected_branch_params
params.require(:protected_branch).permit(:name)
end
end
@@ -22,6 +22,7 @@ class Projects::RepositoriesController < Projects::ApplicationController
if file_path
# Send file to user
response.headers["Content-Length"] = File.open(file_path).size.to_s
send_file file_path
else
render_404
@@ -16,7 +16,7 @@ class Projects::ServicesController < Projects::ApplicationController
end
def update
if @service.update_attributes(params[:service])
if @service.update_attributes(service_params)
redirect_to edit_project_service_path(@project, @service.to_param)
else
render 'edit'
@@ -36,4 +36,11 @@ class Projects::ServicesController < Projects::ApplicationController
def service
@service ||= @project.services.find { |service| service.to_param == params[:id] }
end
def service_params
params.require(:service).permit(
:title, :token, :type, :active, :api_key, :subdomain,
:room, :recipients, :project_url, :username, :password, :api_version
)
end
end
@@ -25,7 +25,7 @@ class Projects::SnippetsController < Projects::ApplicationController
end
def create
@snippet = @project.snippets.build(params[:project_snippet])
@snippet = @project.snippets.build(snippet_params)
@snippet.author = current_user
if @snippet.save
@@ -39,7 +39,7 @@ class Projects::SnippetsController < Projects::ApplicationController
end
def update
if @snippet.update_attributes(params[:project_snippet])
if @snippet.update_attributes(snippet_params)
redirect_to project_snippet_path(@project, @snippet)
else
respond_with(@snippet)
@@ -86,4 +86,8 @@ class Projects::SnippetsController < Projects::ApplicationController
def module_enabled
return render_404 unless @project.snippets_enabled
end
def snippet_params
params.require(:project_snippet).permit(:title, :content, :file_name, :private)
end
end
@@ -27,7 +27,7 @@ class Projects::TeamMembersController < Projects::ApplicationController
def update
@user_project_relation = project.users_projects.find_by(user_id: member)
@user_project_relation.update_attributes(params[:team_member])
@user_project_relation.update_attributes(member_params)
unless @user_project_relation.valid?
flash[:alert] = "User should have at least one role"
@@ -67,4 +67,8 @@ class Projects::TeamMembersController < Projects::ApplicationController
def member
@member ||= User.find_by(username: params[:id])
end
def member_params
params.require(:team_member).permit(:user_id, :project_access)
end
end
+8 -1
View File
@@ -1,7 +1,14 @@
# Controller for viewing a repository's file structure
class Projects::TreeController < Projects::BaseTreeController
def show
return not_found! if tree.entries.empty?
if tree.entries.empty?
if @repository.blob_at(@commit.id, @path)
redirect_to project_blob_path(@project, File.join(@ref, @path)) and return
else
return not_found!
end
end
respond_to do |format|
format.html
+12 -4
View File
@@ -20,7 +20,7 @@ class ProjectsController < ApplicationController
end
def create
@project = ::Projects::CreateService.new(current_user, params[:project]).execute
@project = ::Projects::CreateService.new(current_user, project_params).execute
flash[:notice] = 'Project was successfully created.' if @project.saved?
respond_to do |format|
@@ -29,7 +29,7 @@ class ProjectsController < ApplicationController
end
def update
status = ::Projects::UpdateService.new(@project, current_user, params).execute
status = ::Projects::UpdateService.new(@project, current_user, project_params).execute
respond_to do |format|
if status
@@ -44,7 +44,7 @@ class ProjectsController < ApplicationController
end
def transfer
::Projects::TransferService.new(project, current_user, params[:project]).execute
::Projects::TransferService.new(project, current_user, project_params).execute
end
def show
@@ -85,7 +85,7 @@ class ProjectsController < ApplicationController
redirect_to import_project_path(@project)
end
@project.import_url = params[:project][:import_url]
@project.import_url = project_params[:import_url]
if @project.save
@project.reload
@@ -185,4 +185,12 @@ class ProjectsController < ApplicationController
def user_layout
current_user ? "projects" : "public_projects"
end
def project_params
params.require(:project).permit(
:name, :path, :description, :issues_tracker, :label_list,
:issues_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id,
:wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :merge_requests_template
)
end
end
+12 -1
View File
@@ -13,7 +13,14 @@ class RegistrationsController < Devise::RegistrationsController
def build_resource(hash=nil)
super
self.resource.with_defaults
end
def after_sign_up_path_for resource
new_user_session_path
end
def after_inactive_sign_up_path_for resource
new_user_session_path
end
private
@@ -21,4 +28,8 @@ class RegistrationsController < Devise::RegistrationsController
def signup_enabled?
redirect_to new_user_session_path unless Gitlab.config.gitlab.signup_enabled
end
def sign_up_params
params.require(:user).permit(:username, :email, :name, :password, :password_confirmation)
end
end
+6 -2
View File
@@ -51,7 +51,7 @@ class SnippetsController < ApplicationController
end
def create
@snippet = PersonalSnippet.new(params[:personal_snippet])
@snippet = PersonalSnippet.new(snippet_params)
@snippet.author = current_user
if @snippet.save
@@ -65,7 +65,7 @@ class SnippetsController < ApplicationController
end
def update
if @snippet.update_attributes(params[:personal_snippet])
if @snippet.update_attributes(snippet_params)
redirect_to snippet_path(@snippet)
else
respond_with @snippet
@@ -109,4 +109,8 @@ class SnippetsController < ApplicationController
def set_title
@title = 'Snippets'
end
def snippet_params
params.require(:personal_snippet).permit(:title, :content, :file_name, :private)
end
end
+5 -1
View File
@@ -14,7 +14,7 @@ class UsersGroupsController < ApplicationController
def update
@member = @group.users_groups.find(params[:id])
@member.update_attributes(params[:users_group])
@member.update_attributes(member_params)
end
def destroy
@@ -41,4 +41,8 @@ class UsersGroupsController < ApplicationController
return render_404
end
end
def member_params
params.require(:users_group).permit(:group_access, :user_id)
end
end
+1 -1
View File
@@ -14,7 +14,7 @@ class NotesFinder
project.issues.find(target_id).notes.inc_author.fresh
when "merge_request"
project.merge_requests.find(target_id).mr_and_commit_notes.inc_author.fresh
when "snippet"
when "snippet", "project_snippet"
project.snippets.find(target_id).notes.fresh
else
raise 'invalid target_type'
+11
View File
@@ -180,6 +180,17 @@ module CommitsHelper
return old_lines, new_lines
end
def link_to_browse_code(project, commit)
if current_controller?(:projects, :commits)
if @repo.blob_at(commit.id, @path)
return link_to "Browse File »", project_blob_path(project, tree_join(commit.id, @path)), class: "pull-right"
elsif @path.present?
return link_to "Browse Dir »", project_tree_path(project, tree_join(commit.id, @path)), class: "pull-right"
end
end
link_to "Browse Code »", project_tree_path(project, commit), class: "pull-right"
end
protected
# Private: Returns a link to a person. If the person has a matching user and
+38
View File
@@ -35,4 +35,42 @@ module DashboardHelper
path << "?#{options.to_param}"
path
end
def assigned_entities_count(current_user, entity, scope = nil)
items = current_user.send("assigned_" + entity.pluralize).opened
if scope.kind_of?(Group)
items = items.of_group(scope)
elsif scope.kind_of?(Project)
items = items.of_projects(scope)
end
items.count
end
def authored_entities_count(current_user, entity, scope = nil)
items = current_user.send(entity.pluralize).opened
if scope.kind_of?(Group)
items = items.of_group(scope)
elsif scope.kind_of?(Project)
items = items.of_projects(scope)
end
items.count
end
def authorized_entities_count(current_user, entity, scope = nil)
items = entity.classify.constantize.opened
if scope.kind_of?(Group)
items = items.of_group(scope)
elsif scope.kind_of?(Project)
items = items.of_projects(scope)
else
items = items.of_projects(current_user.authorized_projects)
end
items.count
end
end
+14 -1
View File
@@ -64,7 +64,16 @@ module EventsHelper
project_issue_url(event.project, event.issue)
elsif event.merge_request?
project_merge_request_url(event.project, event.merge_request)
elsif event.note?
if event.note_target
if event.note_commit?
project_commit_path(event.project, event.note_commit_id, anchor: dom_id(event.target))
elsif event.note_project_snippet?
project_snippet_path(event.project, event.note_target)
else
event_note_target_path(event)
end
end
elsif event.push?
if event.push_with_commits?
if event.commits_count > 1
@@ -83,6 +92,10 @@ module EventsHelper
render "events/event_issue", issue: event.issue
elsif event.push?
render "events/event_push", event: event
elsif event.merge_request?
render "events/event_merge_request", merge_request: event.merge_request
elsif event.note?
render "events/event_note", note: event.note
end
end
+1 -1
View File
@@ -138,7 +138,7 @@ module GitlabMarkdownHelper
# If we are at doc/api/README.md and the README.md contains relative links like [Users](users.md)
# this takes the request path(doc/api/README.md), and replaces the README.md with users.md so the path looks like doc/api/users.md
# If we are at doc/api and the README.md shown in below the tree view
# this takes the rquest path(doc/api) and adds users.md so the path looks like doc/api/users.md
# this takes the request path(doc/api) and adds users.md so the path looks like doc/api/users.md
def build_nested_path(path, request_path)
return request_path if path == ""
return path unless request_path
+11
View File
@@ -31,6 +31,17 @@ module GroupsHelper
end
title
end
def group_filter_path(entity, options={})
exist_opts = {
status: params[:status]
}
options = exist_opts.merge(options)
path = request.path
path << "?#{options.to_param}"
path
end
end
+16 -16
View File
@@ -4,10 +4,10 @@ module Emails
@issue = Issue.find(issue_id)
@project = @issue.project
@target_url = project_issue_url(@project, @issue)
set_message_id("issue_#{issue_id}")
mail(from: sender(@issue.author_id),
to: recipient(recipient_id),
subject: subject("#{@issue.title} (##{@issue.iid})"))
mail_new_thread(@issue,
from: sender(@issue.author_id),
to: recipient(recipient_id),
subject: subject("#{@issue.title} (##{@issue.iid})"))
end
def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id, updated_by_user_id)
@@ -15,10 +15,10 @@ module Emails
@previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id
@project = @issue.project
@target_url = project_issue_url(@project, @issue)
set_reference("issue_#{issue_id}")
mail(from: sender(updated_by_user_id),
to: recipient(recipient_id),
subject: subject("#{@issue.title} (##{@issue.iid})"))
mail_answer_thread(@issue,
from: sender(updated_by_user_id),
to: recipient(recipient_id),
subject: subject("#{@issue.title} (##{@issue.iid})"))
end
def closed_issue_email(recipient_id, issue_id, updated_by_user_id)
@@ -26,10 +26,10 @@ module Emails
@project = @issue.project
@updated_by = User.find updated_by_user_id
@target_url = project_issue_url(@project, @issue)
set_reference("issue_#{issue_id}")
mail(from: sender(updated_by_user_id),
to: recipient(recipient_id),
subject: subject("#{@issue.title} (##{@issue.iid})"))
mail_answer_thread(@issue,
from: sender(updated_by_user_id),
to: recipient(recipient_id),
subject: subject("#{@issue.title} (##{@issue.iid})"))
end
def issue_status_changed_email(recipient_id, issue_id, status, updated_by_user_id)
@@ -38,10 +38,10 @@ module Emails
@project = @issue.project
@updated_by = User.find updated_by_user_id
@target_url = project_issue_url(@project, @issue)
set_reference("issue_#{issue_id}")
mail(from: sender(updated_by_user_id),
to: recipient(recipient_id),
subject: subject("#{@issue.title} (##{@issue.iid})"))
mail_answer_thread(@issue,
from: sender(updated_by_user_id),
to: recipient(recipient_id),
subject: subject("#{@issue.title} (##{@issue.iid})"))
end
end
end
+25 -13
View File
@@ -4,10 +4,10 @@ module Emails
@merge_request = MergeRequest.find(merge_request_id)
@project = @merge_request.project
@target_url = project_merge_request_url(@project, @merge_request)
set_message_id("merge_request_#{merge_request_id}")
mail(from: sender(@merge_request.author_id),
to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
mail_new_thread(@merge_request,
from: sender(@merge_request.author_id),
to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
end
def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id, updated_by_user_id)
@@ -15,10 +15,10 @@ module Emails
@previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id
@project = @merge_request.project
@target_url = project_merge_request_url(@project, @merge_request)
set_reference("merge_request_#{merge_request_id}")
mail(from: sender(updated_by_user_id),
to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
mail_answer_thread(@merge_request,
from: sender(updated_by_user_id),
to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
end
def closed_merge_request_email(recipient_id, merge_request_id, updated_by_user_id)
@@ -26,20 +26,32 @@ module Emails
@updated_by = User.find updated_by_user_id
@project = @merge_request.project
@target_url = project_merge_request_url(@project, @merge_request)
set_reference("merge_request_#{merge_request_id}")
mail(from: sender(updated_by_user_id),
to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
mail_answer_thread(@merge_request,
from: sender(updated_by_user_id),
to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
end
def merged_merge_request_email(recipient_id, merge_request_id, updated_by_user_id)
@merge_request = MergeRequest.find(merge_request_id)
@project = @merge_request.project
@target_url = project_merge_request_url(@project, @merge_request)
mail_answer_thread(@merge_request,
from: sender(updated_by_user_id),
to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
end
def merge_request_status_email(recipient_id, merge_request_id, status, updated_by_user_id)
@merge_request = MergeRequest.find(merge_request_id)
@mr_status = status
@project = @merge_request.project
@updated_by = User.find updated_by_user_id
@target_url = project_merge_request_url(@project, @merge_request)
set_reference("merge_request_#{merge_request_id}")
mail(from: sender(updated_by_user_id),
to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
subject: subject("#{@merge_request.title} (##{@merge_request.iid}) #{@mr_status}"))
end
end
+12 -11
View File
@@ -5,9 +5,10 @@ module Emails
@commit = @note.noteable
@project = @note.project
@target_url = project_commit_url(@project, @commit, anchor: "note_#{@note.id}")
mail(from: sender(@note.author_id),
to: recipient(recipient_id),
subject: subject("#{@commit.title} (#{@commit.short_id})"))
mail_answer_thread(@commit,
from: sender(@note.author_id),
to: recipient(recipient_id),
subject: subject("#{@commit.title} (#{@commit.short_id})"))
end
def note_issue_email(recipient_id, note_id)
@@ -15,10 +16,10 @@ module Emails
@issue = @note.noteable
@project = @note.project
@target_url = project_issue_url(@project, @issue, anchor: "note_#{@note.id}")
set_reference("issue_#{@issue.id}")
mail(from: sender(@note.author_id),
to: recipient(recipient_id),
subject: subject("#{@issue.title} (##{@issue.iid})"))
mail_answer_thread(@issue,
from: sender(@note.author_id),
to: recipient(recipient_id),
subject: subject("#{@issue.title} (##{@issue.iid})"))
end
def note_merge_request_email(recipient_id, note_id)
@@ -26,10 +27,10 @@ module Emails
@merge_request = @note.noteable
@project = @note.project
@target_url = project_merge_request_url(@project, @merge_request, anchor: "note_#{@note.id}")
set_reference("merge_request_#{@merge_request.id}")
mail(from: sender(@note.author_id),
to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
mail_answer_thread(@merge_request,
from: sender(@note.author_id),
to: recipient(recipient_id),
subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
end
end
end
+38 -8
View File
@@ -1,4 +1,6 @@
class Notify < ActionMailer::Base
include ActionDispatch::Routing::PolymorphicRoutes
include Emails::Issues
include Emails::MergeRequests
include Emails::Notes
@@ -53,14 +55,6 @@ class Notify < ActionMailer::Base
end
end
# Set the Message-ID header field
#
# local_part - The local part of the message ID
#
def set_message_id(local_part)
headers["Message-ID"] = "<#{local_part}@#{Gitlab.config.gitlab.host}>"
end
# Set the References header field
#
# local_part - The local part of the referenced message ID
@@ -93,4 +87,40 @@ class Notify < ActionMailer::Base
subject << extra.join(' | ') if extra.present?
subject
end
# Return a string suitable for inclusion in the 'Message-Id' mail header.
#
# The message-id is generated from the unique URL to a model object.
def message_id(model)
model_name = model.class.model_name.singular_route_key
"<#{model_name}_#{model.id}@#{Gitlab.config.gitlab.host}>"
end
# Send an email that starts a new conversation thread,
# with headers suitable for grouping by thread in email clients.
#
# See: mail_answer_thread
def mail_new_thread(model, headers = {}, &block)
headers['Message-ID'] = message_id(model)
mail(headers, &block)
end
# Send an email that responds to an existing conversation thread,
# with headers suitable for grouping by thread in email clients.
#
# For grouping emails by thread, email clients heuristics require the answers to:
#
# * have a subject that begin by 'Re: '
# * have a 'In-Reply-To' or 'References' header that references the original 'Message-ID'
#
def mail_answer_thread(model, headers = {}, &block)
headers['In-Reply-To'] = message_id(model)
headers['References'] = message_id(model)
if (headers[:subject])
headers[:subject].prepend('Re: ')
end
mail(headers, &block)
end
end
-2
View File
@@ -1,6 +1,4 @@
class Appearance < ActiveRecord::Base
attr_accessible :title, :description, :logo
validates :title, presence: true
validates :description, presence: true
validates :logo, file_size: { maximum: 1000.kilobytes.to_i }
-2
View File
@@ -14,8 +14,6 @@
#
class BroadcastMessage < ActiveRecord::Base
attr_accessible :alert_type, :color, :ends_at, :font, :message, :starts_at
validates :message, presence: true
validates :starts_at, presence: true
validates :ends_at, presence: true
@@ -0,0 +1,31 @@
module TokenAuthenticatable
extend ActiveSupport::Concern
module ClassMethods
def find_by_authentication_token(authentication_token = nil)
if authentication_token
where(authentication_token: authentication_token).first
end
end
end
def ensure_authentication_token
if authentication_token.blank?
self.authentication_token = generate_authentication_token
end
end
def reset_authentication_token!
self.authentication_token = generate_authentication_token
save
end
private
def generate_authentication_token
loop do
token = Devise.friendly_token
break token unless self.class.unscoped.where(authentication_token: token).first
end
end
end
-3
View File
@@ -10,13 +10,10 @@
#
class DeployKeysProject < ActiveRecord::Base
attr_accessible :key_id, :project_id
belongs_to :project
belongs_to :deploy_key
validates :deploy_key_id, presence: true
validates :deploy_key_id, uniqueness: { scope: [:project_id], message: "already exists in project" }
validates :project_id, presence: true
end
-8
View File
@@ -10,16 +10,8 @@
#
class Email < ActiveRecord::Base
attr_accessible :email, :user_id
#
# Relations
#
belongs_to :user
#
# Validations
#
validates :user_id, presence: true
validates :email, presence: true, email: { strict_mode: true }, uniqueness: true
validate :unique_email, if: ->(email) { email.email_changed? }
+5 -3
View File
@@ -15,9 +15,6 @@
#
class Event < ActiveRecord::Base
attr_accessible :project, :action, :data, :author_id, :project_id,
:target_id, :target_type
default_scope { where.not(author_id: nil) }
CREATED = 1
@@ -33,6 +30,7 @@ class Event < ActiveRecord::Base
delegate :name, :email, to: :author, prefix: true, allow_nil: true
delegate :title, to: :issue, prefix: true, allow_nil: true
delegate :title, to: :merge_request, prefix: true, allow_nil: true
delegate :title, to: :note, prefix: true, allow_nil: true
belongs_to :author, class_name: "User"
belongs_to :project
@@ -150,6 +148,10 @@ class Event < ActiveRecord::Base
target if target_type == "MergeRequest"
end
def note
target if target_type == "Note"
end
def action_name
if closed?
"closed"
-4
View File
@@ -10,10 +10,6 @@
#
class ForkedProjectLink < ActiveRecord::Base
attr_accessible :forked_from_project_id, :forked_to_project_id
# Relations
belongs_to :forked_to_project, class_name: Project
belongs_to :forked_from_project, class_name: Project
end
-2
View File
@@ -1,6 +1,4 @@
class GitHook < ActiveRecord::Base
attr_accessible :deny_delete_tag, :delete_branch_regex, :commit_message_regex, :force_push_regex
belongs_to :project
validates :project, presence: true
-5
View File
@@ -19,19 +19,14 @@ require 'file_size_validator'
class Group < Namespace
has_many :users_groups, dependent: :destroy
has_many :users, through: :users_groups
has_many :project_group_links, dependent: :destroy
has_many :shared_projects, through: :project_group_links, source: :project
attr_accessible :ldap_cn, :ldap_access
validates :ldap_access,
inclusion: { in: UsersGroup.group_access_roles.values },
presence: true,
if: ->(group) { group.ldap_cn.present? }
attr_accessible :avatar
validate :avatar_type, if: ->(user) { user.avatar_changed? }
validates :avatar, file_size: { maximum: 100.kilobytes.to_i }
+95
View File
@@ -0,0 +1,95 @@
class GroupMilestone
def initialize(title, milestones)
@title = title
@milestones = milestones
end
def title
@title
end
def safe_title
@title.parameterize
end
def milestones
@milestones
end
def projects
milestones.map { |milestone| milestone.project }
end
def issue_count
milestones.map { |milestone| milestone.issues.count }.sum
end
def merge_requests_count
milestones.map { |milestone| milestone.merge_requests.count }.sum
end
def open_items_count
milestones.map { |milestone| milestone.open_items_count }.sum
end
def closed_items_count
milestones.map { |milestone| milestone.closed_items_count }.sum
end
def total_items_count
milestones.map { |milestone| milestone.total_items_count }.sum
end
def percent_complete
((closed_items_count * 100) / total_items_count).abs
rescue ZeroDivisionError
100
end
def state
state = milestones.map { |milestone| milestone.state }
if state.count('active') == state.size
'active'
else
'closed'
end
end
def active?
state == 'active'
end
def closed?
state == 'closed'
end
def issues
@group_issues ||= milestones.map { |milestone| milestone.issues }.flatten.group_by(&:state)
end
def merge_requests
@group_merge_requests ||= milestones.map { |milestone| milestone.merge_requests }.flatten.group_by(&:state)
end
def participants
milestones.map { |milestone| milestone.participants.uniq }.reject(&:empty?).flatten
end
def opened_issues
issues.values_at("opened", "reopened").compact.flatten
end
def closed_issues
issues['closed']
end
def opened_merge_requests
merge_requests.values_at("opened", "reopened").compact.flatten
end
def closed_merge_requests
merge_requests.values_at("closed", "merged", "locked").compact.flatten
end
end
-3
View File
@@ -33,9 +33,6 @@ class Issue < ActiveRecord::Base
scope :of_group, ->(group) { where(project_id: group.project_ids) }
scope :of_user_team, ->(team) { where(project_id: team.project_ids, assignee_id: team.member_ids) }
attr_accessible :title, :assignee_id, :position, :description,
:milestone_id, :label_list, :state_event
acts_as_taggable_on :labels
scope :cared, ->(user) { where(assignee_id: user) }
-2
View File
@@ -19,8 +19,6 @@ class Key < ActiveRecord::Base
belongs_to :user
attr_accessible :key, :title
before_validation :strip_white_space, :generate_fingerpint
validates :title, presence: true, length: { within: 0..255 }
+4 -6
View File
@@ -36,10 +36,6 @@ class MergeRequest < ActiveRecord::Base
delegate :commits, :diffs, :last_commit, :last_commit_short_sha, to: :merge_request_diff, prefix: nil
attr_accessible :title, :assignee_id, :source_project_id, :source_branch,
:target_project_id, :target_branch, :milestone_id,
:state_event, :description, :label_list
attr_accessor :should_remove_source_branch
# When this attribute is true some MR validation is ignored
@@ -62,11 +58,11 @@ class MergeRequest < ActiveRecord::Base
transition closed: :reopened
end
event :lock do
event :lock_mr do
transition [:reopened, :opened] => :locked
end
event :unlock do
event :unlock_mr do
transition locked: :reopened
end
@@ -297,6 +293,8 @@ class MergeRequest < ActiveRecord::Base
message << title.to_s
message << "\n\n"
message << description.to_s
message << "\n\n"
message << "See merge request !#{iid}"
message
end
-2
View File
@@ -22,8 +22,6 @@ class MergeRequestDiff < ActiveRecord::Base
belongs_to :merge_request
attr_accessible :state, :st_commits, :st_diffs
delegate :target_branch, :source_branch, to: :merge_request, prefix: nil
state_machine :state, initial: :empty do
-2
View File
@@ -16,8 +16,6 @@
class Milestone < ActiveRecord::Base
include InternalId
attr_accessible :title, :description, :due_date, :state_event
belongs_to :project
has_many :issues
has_many :merge_requests
+2 -4
View File
@@ -16,8 +16,6 @@
class Namespace < ActiveRecord::Base
include Gitlab::ShellAdapter
attr_accessible :name, :description, :path
has_many :projects, dependent: :destroy
belongs_to :owner, class_name: "User"
@@ -25,12 +23,12 @@ class Namespace < ActiveRecord::Base
validates :name, presence: true, uniqueness: true,
length: { within: 0..255 },
format: { with: Gitlab::Regex.name_regex,
message: "only letters, digits, spaces & '_' '-' '.' allowed." }
message: Gitlab::Regex.name_regex_message }
validates :description, length: { within: 0..255 }
validates :path, uniqueness: { case_sensitive: false }, presence: true, length: { within: 1..255 },
exclusion: { in: Gitlab::Blacklist.path },
format: { with: Gitlab::Regex.path_regex,
message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
message: Gitlab::Regex.path_regex_message }
delegate :name, to: :owner, allow_nil: true, prefix: true
+6 -8
View File
@@ -25,8 +25,6 @@ class Note < ActiveRecord::Base
default_value_for :system, false
attr_accessible :note, :noteable, :noteable_id, :noteable_type, :project_id,
:attachment, :line_code, :commit_id
attr_mentionable :note
belongs_to :project
@@ -63,13 +61,13 @@ class Note < ActiveRecord::Base
def create_status_change_note(noteable, project, author, status, source)
body = "_Status changed to #{status}#{' by ' + source.gfm_reference if source}_"
create({
create(
noteable: noteable,
project: project,
author: author,
note: body,
system: true
}, without_protection: true)
)
end
# +noteable+ was referenced from +mentioner+, by including GFM in either +mentioner+'s description or an associated Note.
@@ -88,7 +86,7 @@ class Note < ActiveRecord::Base
note_options.merge!(noteable: noteable)
end
create(note_options, without_protection: true)
create(note_options)
end
def create_milestone_change_note(noteable, project, author, milestone)
@@ -98,13 +96,13 @@ class Note < ActiveRecord::Base
"_Milestone changed to #{milestone.title}_"
end
create({
create(
noteable: noteable,
project: project,
author: author,
note: body,
system: true
}, without_protection: true)
)
end
def create_assignee_change_note(noteable, project, author, assignee)
@@ -116,7 +114,7 @@ class Note < ActiveRecord::Base
author: author,
note: body,
system: true
}, without_protection: true)
})
end
def discussions_from_notes(notes)
+20 -17
View File
@@ -27,24 +27,20 @@
class Project < ActiveRecord::Base
include Gitlab::ShellAdapter
include Gitlab::VisibilityLevel
include Gitlab::ConfigHelper
extend Gitlab::ConfigHelper
extend Enumerize
default_value_for :archived, false
default_value_for :issues_enabled, true
default_value_for :merge_requests_enabled, true
default_value_for :wiki_enabled, true
default_value_for :visibility_level, gitlab_config_features.visibility_level
default_value_for :issues_enabled, gitlab_config_features.issues
default_value_for :merge_requests_enabled, gitlab_config_features.merge_requests
default_value_for :wiki_enabled, gitlab_config_features.wiki
default_value_for :wall_enabled, false
default_value_for :snippets_enabled, true
default_value_for :snippets_enabled, gitlab_config_features.snippets
ActsAsTaggableOn.strict_case_match = true
attr_accessible :name, :path, :description, :issues_tracker, :label_list,
:issues_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id,
:wiki_enabled, :visibility_level, :import_url, :last_activity_at,
:merge_requests_template, as: [:default, :admin]
attr_accessible :namespace_id, :creator_id, as: :admin
acts_as_taggable_on :labels, :issues_default_labels
attr_accessor :new_default_branch
@@ -101,13 +97,16 @@ class Project < ActiveRecord::Base
validates :description, length: { maximum: 2000 }, allow_blank: true
validates :name, presence: true, length: { within: 0..255 },
format: { with: Gitlab::Regex.project_name_regex,
message: "only letters, digits, spaces & '_' '-' '.' allowed. Letter or digit should be first" }
message: Gitlab::Regex.project_regex_message }
validates :path, presence: true, length: { within: 0..255 },
exclusion: { in: Gitlab::Blacklist.path },
format: { with: Gitlab::Regex.path_regex,
message: "only letters, digits & '_' '-' '.' allowed. Letter or digit should be first" }
message: Gitlab::Regex.path_regex_message }
validates :issues_enabled, :merge_requests_enabled,
:wiki_enabled, inclusion: { in: [true, false] }
validates :visibility_level,
exclusion: { in: gitlab_config.restricted_visibility_levels },
if: -> { gitlab_config.restricted_visibility_levels.any? }
validates :issues_tracker_id, length: { maximum: 255 }, allow_blank: true
validates :namespace, presence: true
validates_uniqueness_of :name, scope: :namespace_id
@@ -251,7 +250,7 @@ class Project < ActiveRecord::Base
end
def check_limit
unless creator.can_create_project?
unless creator.can_create_project? or namespace.kind == 'group'
errors[:limit_reached] << ("Your project limit is #{creator.projects_limit} projects! Please contact your administrator to increase it")
end
rescue
@@ -263,7 +262,7 @@ class Project < ActiveRecord::Base
end
def web_url
[Gitlab.config.gitlab.url, path_with_namespace].join("/")
[gitlab_config.url, path_with_namespace].join("/")
end
def web_url_without_protocol
@@ -403,7 +402,11 @@ class Project < ActiveRecord::Base
services.each do |service|
# Call service hook only if it is active
service.execute(data) if service.active
begin
service.execute(data) if service.active
rescue => e
logger.error(e)
end
end
end
@@ -488,7 +491,7 @@ class Project < ActiveRecord::Base
end
def http_url_to_repo
[Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('')
[gitlab_config.url, "/", path_with_namespace, ".git"].join('')
end
# Check if current branch name is marked as protected in the system
-2
View File
@@ -18,8 +18,6 @@
class ProjectHook < WebHook
belongs_to :project
attr_accessible :push_events, :issues_events, :merge_requests_events, :tag_push_events
scope :push_hooks, -> { where(push_events: true) }
scope :tag_push_hooks, -> { where(tag_push_events: true) }
scope :issue_hooks, -> { where(issues_events: true) }
@@ -18,8 +18,6 @@
#
class AssemblaService < Service
attr_accessible :subdomain
include HTTParty
validates :token, presence: true, if: :activated?
@@ -18,8 +18,6 @@
#
class CampfireService < Service
attr_accessible :subdomain, :room
validates :token, presence: true, if: :activated?
def title
@@ -18,8 +18,6 @@
#
class EmailsOnPushService < Service
attr_accessible :recipients
validates :recipients, presence: true, if: :activated?
def title
@@ -18,8 +18,6 @@
#
class GitlabCiService < CiService
attr_accessible :project_url
validates :project_url, presence: true, if: :activated?
validates :token, presence: true, if: :activated?
@@ -18,8 +18,6 @@
#
class HipchatService < Service
attr_accessible :room
validates :token, presence: true, if: :activated?
def title
@@ -18,8 +18,6 @@
#
class JenkinsService < CiService
attr_accessible :project_url
validates :project_url, presence: true, if: :activated?
delegate :execute, to: :service_hook, prefix: nil
@@ -20,9 +20,7 @@
# api_version :string(255)
class JiraService < Service
include HTTParty
attr_accessible :project_url, :username, :password, :api_version
validates :username, :password, presence: true, if: :activated?
before_validation :set_api_version
@@ -18,9 +18,6 @@
#
class SlackService < Service
attr_accessible :room
attr_accessible :subdomain
validates :room, presence: true, if: :activated?
validates :subdomain, presence: true, if: :activated?
validates :token, presence: true, if: :activated?
-2
View File
@@ -12,8 +12,6 @@
class ProtectedBranch < ActiveRecord::Base
include Gitlab::ShellAdapter
attr_accessible :name
belongs_to :project
validates :name, presence: true
validates :project, presence: true
+21
View File
@@ -242,4 +242,25 @@ class Repository
branches
end
end
def contributors
log = graph_log.group_by { |i| i[:author_email] }
log.map do |email, contributions|
contributor = Gitlab::Contributor.new
contributor.email = email
contributions.each do |contribution|
if contributor.name.blank?
contributor.name = contribution[:author_name]
end
contributor.commits += 1
contributor.additions += contribution[:additions] || 0
contributor.deletions += contribution[:deletions] || 0
end
contributor
end
end
end
-2
View File
@@ -22,8 +22,6 @@
class Service < ActiveRecord::Base
default_value_for :active, false
attr_accessible :title, :token, :type, :active, :api_key
belongs_to :project
has_one :service_hook
-2
View File
@@ -18,8 +18,6 @@
class Snippet < ActiveRecord::Base
include Linguist::BlobHelper
attr_accessible :title, :content, :file_name, :expires_at, :private
default_value_for :private, true
belongs_to :author, class_name: "User"
+13 -31
View File
@@ -50,31 +50,25 @@ require 'carrierwave/orm/activerecord'
require 'file_size_validator'
class User < ActiveRecord::Base
include Gitlab::ConfigHelper
extend Gitlab::ConfigHelper
include TokenAuthenticatable
default_value_for :admin, false
default_value_for :can_create_group, true
default_value_for :can_create_group, gitlab_config.default_can_create_group
default_value_for :can_create_team, false
default_value_for :hide_no_ssh_key, false
default_value_for :projects_limit, gitlab_config.default_projects_limit
default_value_for :theme_id, gitlab_config.default_theme
devise :database_authenticatable, :token_authenticatable, :lockable, :async,
devise :database_authenticatable, :lockable, :async,
:recoverable, :rememberable, :trackable, :validatable, :omniauthable, :confirmable, :registerable
attr_accessible :email, :password, :password_confirmation, :remember_me, :bio, :name, :username,
:skype, :linkedin, :twitter, :website_url, :color_scheme_id, :theme_id, :force_random_password,
:extern_uid, :provider, :password_expires_at, :avatar, :hide_no_ssh_key,
as: [:default, :admin]
attr_accessible :projects_limit, :can_create_group,
as: :admin
attr_accessor :force_random_password
# Virtual attribute for authenticating by either username or email
attr_accessor :login
# Add login to attr_accessible
attr_accessible :login
#
# Relations
#
@@ -120,7 +114,7 @@ class User < ActiveRecord::Base
validates :username, presence: true, uniqueness: { case_sensitive: false },
exclusion: { in: Gitlab::Blacklist.path },
format: { with: Gitlab::Regex.username_regex,
message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
message: Gitlab::Regex.username_regex_message }
validates :notification_level, inclusion: { in: Notification.notification_levels }, presence: true
validate :namespace_uniq, if: ->(user) { user.username_changed? }
@@ -223,20 +217,8 @@ class User < ActiveRecord::Base
where('users.username = ? OR users.id = ?', name_or_id.to_s, name_or_id.to_i).first
end
def build_user(attrs = {}, options= {})
if options[:as] == :admin
User.new(defaults.merge(attrs.symbolize_keys), options)
else
User.new(attrs, options).with_defaults
end
end
def defaults
{
projects_limit: Gitlab.config.gitlab.default_projects_limit,
can_create_group: Gitlab.config.gitlab.default_can_create_group,
theme_id: Gitlab.config.gitlab.default_theme
}
def build_user(attrs = {})
User.new(attrs)
end
end
@@ -315,7 +297,7 @@ class User < ActiveRecord::Base
end
def can_change_username?
Gitlab.config.gitlab.username_changing_enabled
gitlab_config.username_changing_enabled
end
def can_create_project?
@@ -490,7 +472,7 @@ class User < ActiveRecord::Base
def avatar_url(size = nil)
if avatar.present?
URI::join(Gitlab.config.gitlab.url, avatar.url).to_s
URI::join(gitlab_config.url, avatar.url).to_s
else
GravatarService.new.execute(email, size)
end
-2
View File
@@ -19,8 +19,6 @@ class UsersGroup < ActiveRecord::Base
Gitlab::Access.options_with_owner
end
attr_accessible :group_access, :user_id
belongs_to :user
belongs_to :group
+1 -3
View File
@@ -16,8 +16,6 @@ class UsersProject < ActiveRecord::Base
include Notifiable
include Gitlab::Access
attr_accessible :user, :user_id, :project_access
belongs_to :user
belongs_to :project
@@ -126,7 +124,7 @@ class UsersProject < ActiveRecord::Base
author_id: self.user.id
)
notification_service.new_team_member(self)
notification_service.new_team_member(self) unless owner?
system_hook_service.execute_hooks_for(self, :create)
end
-2
View File
@@ -22,8 +22,6 @@ class WebHook < ActiveRecord::Base
default_value_for :issues_events, false
default_value_for :merge_requests_events, false
attr_accessible :url
# HTTParty timeout
default_timeout 10
+4 -1
View File
@@ -25,7 +25,10 @@ module Files
file_path = path
unless file_name =~ Gitlab::Regex.path_regex
return error("Your changes could not be committed, because file name contains not allowed characters")
return error(
'Your changes could not be committed, because the file name ' +
Gitlab::Regex.path_regex_message
)
end
blob = repository.blob_at_branch(ref, file_path)
+1
View File
@@ -4,6 +4,7 @@ module Issues
if issue.reopen
event_service.reopen_issue(issue, current_user)
create_note(issue)
notification_service.reopen_issue(issue, current_user)
execute_hooks(issue, 'reopen')
end
+2 -2
View File
@@ -1,7 +1,7 @@
module Issues
class UpdateService < Issues::BaseService
def execute(issue)
state = params.delete('state_event') || params.delete(:state_event)
state = params[:state_event]
case state
when 'reopen'
@@ -10,7 +10,7 @@ module Issues
Issues::CloseService.new(project, current_user, {}).execute(issue)
end
if params.present? && issue.update_attributes(params)
if params.present? && issue.update_attributes(params.except(:state_event))
issue.reset_events_cache
if issue.previous_changes.include?('milestone_id')
@@ -6,7 +6,7 @@ module MergeRequests
# Called when you do merge via GitLab UI
class AutoMergeService < BaseMergeService
def execute(merge_request, current_user, commit_message)
merge_request.lock
merge_request.lock_mr
if Gitlab::Satellite::MergeAction.new(current_user, merge_request).merge!(commit_message)
merge_request.merge
@@ -17,11 +17,11 @@ module MergeRequests
true
else
merge_request.unlock
merge_request.unlock_mr
false
end
rescue
merge_request.unlock if merge_request.locked?
merge_request.unlock_mr if merge_request.locked?
merge_request.mark_as_unmergeable
false
end
@@ -3,6 +3,7 @@ module MergeRequests
def execute(merge_request)
if merge_request.reopen
event_service.reopen_mr(merge_request, current_user)
notification_service.reopen_mr(merge_request, current_user)
create_note(merge_request)
execute_hooks(merge_request)
merge_request.reload_code
@@ -7,10 +7,10 @@ module MergeRequests
def execute(merge_request)
# We dont allow change of source/target projects
# after merge request was created
params.delete(:source_project_id)
params.delete(:target_project_id)
params.except!(:source_project_id)
params.except!(:target_project_id)
state = params.delete('state_event') || params.delete(:state_event)
state = params[:state_event]
case state
when 'reopen'
@@ -19,7 +19,7 @@ module MergeRequests
MergeRequests::CloseService.new(project, current_user, {}).execute(merge_request)
end
if params.present? && merge_request.update_attributes(params)
if params.present? && merge_request.update_attributes(params.except(:state_event))
merge_request.reset_events_cache
if merge_request.previous_changes.include?('milestone_id')
+26
View File
@@ -0,0 +1,26 @@
module Milestones
class GroupService < Milestones::BaseService
def initialize(project_milestones)
@project_milestones = project_milestones.group_by(&:title)
end
def execute
build(@project_milestones)
end
def milestone(title)
if title
group_milestone = @project_milestones[title].group_by(&:title)
build(group_milestone).first
else
nil
end
end
private
def build(milestone)
milestone.map{ |title, milestones| GroupMilestone.new(title, milestones) }
end
end
end
+2 -2
View File
@@ -1,7 +1,7 @@
module Milestones
class UpdateService < Milestones::BaseService
def execute(milestone)
state = params.delete('state_event') || params.delete(:state_event)
state = params[:state_event]
case state
when 'activate'
@@ -11,7 +11,7 @@ module Milestones
end
if params.present?
milestone.update_attributes(params)
milestone.update_attributes(params.except(:state_event))
end
milestone
+31 -2
View File
@@ -80,6 +80,10 @@ class NotificationService
close_resource_email(merge_request, merge_request.target_project, current_user, 'closed_merge_request_email')
end
def reopen_issue(issue, current_user)
reopen_resource_email(issue, issue.project, current_user, 'issue_status_changed_email', 'reopened')
end
# When we merge a merge request we should send next emails:
#
# * merge_request author if their notification level is not Disabled
@@ -89,12 +93,17 @@ class NotificationService
def merge_mr(merge_request, current_user)
recipients = reject_muted_users([merge_request.author, merge_request.assignee], merge_request.target_project)
recipients = recipients.concat(project_watchers(merge_request.target_project)).uniq
recipients.delete(current_user)
recipients.each do |recipient|
mailer.merged_merge_request_email(recipient.id, merge_request.id, current_user.id)
end
end
def reopen_mr(merge_request, current_user)
reopen_resource_email(merge_request, merge_request.target_project, current_user, 'merge_request_status_email', 'reopened')
end
# Notify new user with email after creation
def new_user(user)
# Don't email omniauth created users
@@ -301,7 +310,9 @@ class NotificationService
end
def reassign_resource_email(target, project, current_user, method)
recipients = User.where(id: [target.assignee_id, target.assignee_id_was])
assignee_id_was = previous_record(target, "assignee_id")
recipients = User.where(id: [target.assignee_id, assignee_id_was])
# Add watchers to email list
recipients = recipients.concat(project_watchers(project))
@@ -313,11 +324,29 @@ class NotificationService
recipients.delete(current_user)
recipients.each do |recipient|
mailer.send(method, recipient.id, target.id, target.assignee_id_was, current_user.id)
mailer.send(method, recipient.id, target.id, assignee_id_was, current_user.id)
end
end
def reopen_resource_email(target, project, current_user, method, status)
recipients = reject_muted_users([target.author, target.assignee], project)
recipients = recipients.concat(project_watchers(project)).uniq
recipients.delete(current_user)
recipients.each do |recipient|
mailer.send(method, recipient.id, target.id, status, current_user.id)
end
end
def mailer
Notify.delay
end
def previous_record(object, attribute)
if object && attribute
if object.previous_changes.include?(attribute)
object.previous_changes[attribute].first
end
end
end
end
+7 -20
View File
@@ -5,27 +5,13 @@ module Projects
end
def execute
# get namespace id
namespace_id = params.delete(:namespace_id)
@project = Project.new(params)
# check that user is allowed to set specified visibility_level
# Reset visibility levet if is not allowed to set it
unless Gitlab::VisibilityLevel.allowed_for?(current_user, params[:visibility_level])
params.delete(:visibility_level)
@project.visibility_level = default_features.visibility_level
end
# Load default feature settings
default_features = Gitlab.config.gitlab.default_projects_features
default_opts = {
issues_enabled: default_features.issues,
wiki_enabled: default_features.wiki,
snippets_enabled: default_features.snippets,
merge_requests_enabled: default_features.merge_requests,
visibility_level: default_features.visibility_level
}.stringify_keys
@project = Project.new(default_opts.merge(params))
# Parametrize path for project
#
# Ex.
@@ -33,13 +19,14 @@ module Projects
#
@project.path = @project.name.dup.parameterize unless @project.path.present?
# get namespace id
namespace_id = params[:namespace_id]
if namespace_id
# Find matching namespace and check if it allowed
# for current user if namespace_id passed.
if allowed_namespace?(current_user, namespace_id)
@project.namespace_id = namespace_id
else
unless allowed_namespace?(current_user, namespace_id)
@project.namespace_id = nil
deny_namespace
return @project
end

Some files were not shown because too many files have changed in this diff Show More