Commit eefb27f5 authored by Sebastian Ziebell's avatar Sebastian Ziebell

Merge branch 'master' into fixes/api

Conflicts:
	spec/requests/api/projects_spec.rb
parents 1b97a2ee b7ac654b
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
.rbx/ .rbx/
db/*.sqlite3 db/*.sqlite3
db/*.sqlite3-journal db/*.sqlite3-journal
log/*.log log/*.log*
tmp/ tmp/
.sass-cache/ .sass-cache/
coverage/* coverage/*
...@@ -20,6 +20,7 @@ config/database.yml ...@@ -20,6 +20,7 @@ config/database.yml
config/initializers/omniauth.rb config/initializers/omniauth.rb
config/unicorn.rb config/unicorn.rb
config/resque.yml config/resque.yml
config/aws.yml
db/data.yml db/data.yml
.idea .idea
.DS_Store .DS_Store
......
...@@ -70,6 +70,9 @@ gem "github-markup", "~> 0.7.4", require: 'github/markup' ...@@ -70,6 +70,9 @@ gem "github-markup", "~> 0.7.4", require: 'github/markup'
# Servers # Servers
gem "unicorn", "~> 4.4.0" gem "unicorn", "~> 4.4.0"
# State machine
gem "state_machine"
# Issue tags # Issue tags
gem "acts-as-taggable-on", "2.3.3" gem "acts-as-taggable-on", "2.3.3"
......
...@@ -425,6 +425,7 @@ GEM ...@@ -425,6 +425,7 @@ GEM
rack (~> 1.0) rack (~> 1.0)
tilt (~> 1.1, != 1.3.0) tilt (~> 1.1, != 1.3.0)
stamp (0.3.0) stamp (0.3.0)
state_machine (1.1.2)
temple (0.5.5) temple (0.5.5)
test_after_commit (0.0.1) test_after_commit (0.0.1)
therubyracer (0.10.2) therubyracer (0.10.2)
...@@ -536,6 +537,7 @@ DEPENDENCIES ...@@ -536,6 +537,7 @@ DEPENDENCIES
slim slim
spinach-rails spinach-rails
stamp stamp
state_machine
test_after_commit test_after_commit
therubyracer therubyracer
thin thin
......
...@@ -49,6 +49,10 @@ $ -> ...@@ -49,6 +49,10 @@ $ ->
# Bottom tooltip # Bottom tooltip
$('.has_bottom_tooltip').tooltip(placement: 'bottom') $('.has_bottom_tooltip').tooltip(placement: 'bottom')
# Form submitter
$('.trigger-submit').on 'change', ->
$(@).parents('form').submit()
# Flash # Flash
if (flash = $("#flash-container")).length > 0 if (flash = $("#flash-container")).length > 0
flash.click -> $(@).slideUp("slow") flash.click -> $(@).slideUp("slow")
......
...@@ -27,7 +27,7 @@ class MergeRequest ...@@ -27,7 +27,7 @@ class MergeRequest
this.$el.find(selector) this.$el.find(selector)
initMergeWidget: -> initMergeWidget: ->
this.showState( @opts.current_state ) this.showState( @opts.current_status )
if this.$('.automerge_widget').length and @opts.check_enable if this.$('.automerge_widget').length and @opts.check_enable
$.get @opts.url_to_automerge_check, (data) => $.get @opts.url_to_automerge_check, (data) =>
......
...@@ -63,7 +63,7 @@ ...@@ -63,7 +63,7 @@
color: $style_color; color: $style_color;
text-shadow: 0 1px 1px #FFF; text-shadow: 0 1px 1px #FFF;
font-family: 'Yanone', sans-serif; font-family: 'Yanone', sans-serif;
font-size: 26px; font-size: 24px;
line-height: 42px; line-height: 36px;
font-weight: normal; font-weight: normal;
} }
...@@ -337,7 +337,6 @@ ...@@ -337,7 +337,6 @@
*/ */
.commit { .commit {
.browse_code_link_holder { .browse_code_link_holder {
@extend .span2;
float: right; float: right;
} }
......
...@@ -5,15 +5,16 @@ ...@@ -5,15 +5,16 @@
header { header {
&.navbar-gitlab { &.navbar-gitlab {
.navbar-inner { .navbar-inner {
height: 45px; height: 40px;
padding: 5px; padding: 3px;
background: #F1F1F1; background: #F1F1F1;
filter: none;
.nav > li > a { .nav > li > a {
color: $style_color; color: $style_color;
text-shadow: 0 1px 0 #fff; text-shadow: 0 1px 0 #fff;
font-size: 18px; font-size: 16px;
padding: 12px; padding: 10px;
} }
/** NAV block with links and profile **/ /** NAV block with links and profile **/
...@@ -25,7 +26,6 @@ header { ...@@ -25,7 +26,6 @@ header {
} }
z-index: 10; z-index: 10;
/*height: 60px;*/
/** /**
* *
...@@ -34,7 +34,7 @@ header { ...@@ -34,7 +34,7 @@ header {
*/ */
.app_logo { .app_logo {
float: left; float: left;
margin-right: 15px; margin-right: 9px;
position: relative; position: relative;
top: -5px; top: -5px;
padding-top: 5px; padding-top: 5px;
...@@ -42,10 +42,10 @@ header { ...@@ -42,10 +42,10 @@ header {
a { a {
float: left; float: left;
padding: 0px; padding: 0px;
margin: 0 10px; margin: 0 6px;
h1 { h1 {
background: url('logo_dark.png') no-repeat 0px 2px; background: url('logo_dark.png') no-repeat center 1px;
float: left; float: left;
height: 40px; height: 40px;
width: 40px; width: 40px;
...@@ -79,7 +79,6 @@ header { ...@@ -79,7 +79,6 @@ header {
.search { .search {
margin-right: 45px; margin-right: 45px;
margin-left: 10px; margin-left: 10px;
margin-top: 2px;
.search-input { .search-input {
@extend .span2; @extend .span2;
...@@ -105,7 +104,7 @@ header { ...@@ -105,7 +104,7 @@ header {
.account-box { .account-box {
position: absolute; position: absolute;
right: 0; right: 0;
top: 6px; top: 4px;
z-index: 10000; z-index: 10000;
width: 128px; width: 128px;
font-size: 11px; font-size: 11px;
...@@ -228,6 +227,7 @@ header { ...@@ -228,6 +227,7 @@ header {
.search-input { .search-input {
background-color: #D2D5DA; background-color: #D2D5DA;
background-color: rgba(255, 255, 255, 0.5); background-color: rgba(255, 255, 255, 0.5);
border: 1px solid #AAA;
&:focus { &:focus {
background-color: white; background-color: white;
...@@ -240,13 +240,16 @@ header { ...@@ -240,13 +240,16 @@ header {
.app_logo { .app_logo {
a { a {
h1 { h1 {
background: url('logo_white.png') no-repeat center center; background: url('logo_white.png') no-repeat center 1px;
color: #fff; color: #fff;
text-shadow: 0 1px 1px #111; text-shadow: 0 1px 1px #111;
} }
} }
} }
.project_name { .project_name {
a {
color: #FFF;
}
color: #fff; color: #fff;
text-shadow: 0 1px 1px #111; text-shadow: 0 1px 1px #111;
} }
...@@ -261,11 +264,11 @@ header { ...@@ -261,11 +264,11 @@ header {
.separator { .separator {
float: left; float: left;
height: 60px; height: 46px;
width: 1px; width: 1px;
background: white; background: white;
border-left: 1px solid #DDD; border-left: 1px solid #DDD;
margin-top: -10px; margin-top: -3px;
margin-left: 10px; margin-left: 10px;
margin-right: 10px; margin-right: 10px;
} }
......
...@@ -115,3 +115,7 @@ ul.nav.nav-projects-tabs { ...@@ -115,3 +115,7 @@ ul.nav.nav-projects-tabs {
} }
} }
} }
.team_member_row form {
margin: 0px;
}
...@@ -8,56 +8,21 @@ ...@@ -8,56 +8,21 @@
* *
*/ */
.ui_mars { .ui_mars {
/* /*
* Application Header * Application Header
* *
*/ */
header { header {
@extend .header-dark;
&.navbar-gitlab { &.navbar-gitlab {
.navbar-inner { .navbar-inner {
background: #474D57 url('bg-header.png') repeat-x bottom; background: #474D57;
border-bottom: 1px solid #444; border-bottom: 1px solid #373D47;
.nav > li > a {
color: #eee;
text-shadow: 0 1px 0 #444;
}
}
}
.search {
float: right;
margin-right: 45px;
.search-input {
border: 1px solid rgba(0, 0, 0, 0.7);
background-color: #D2D5DA;
background-color: rgba(255, 255, 255, 0.5);
&:focus {
background-color: white;
}
}
}
.search-input::-webkit-input-placeholder {
color: #666;
}
.app_logo { .app_logo {
a {
h1 {
background: url('logo_white.png') no-repeat center center;
color: #eee;
text-shadow: 0 1px 1px #111;
}
}
&:hover { &:hover {
background-color: #41464e; background-color: #373D47;
} }
} }
.project_name {
color: #eee;
text-shadow: 0 1px 1px #111;
} }
} }
...@@ -65,9 +30,5 @@ ...@@ -65,9 +30,5 @@
background: #31363E; background: #31363E;
border-left: 1px solid #666; border-left: 1px solid #666;
} }
}
/*
* End of Application Header
*
*/
} }
...@@ -14,7 +14,7 @@ class MergeRequestsLoadContext < BaseContext ...@@ -14,7 +14,7 @@ class MergeRequestsLoadContext < BaseContext
end end
merge_requests = merge_requests.page(params[:page]).per(20) merge_requests = merge_requests.page(params[:page]).per(20)
merge_requests = merge_requests.includes(:author, :project).order("closed, created_at desc") merge_requests = merge_requests.includes(:author, :project).order("state, created_at desc")
# Filter by specific assignee_id (or lack thereof)? # Filter by specific assignee_id (or lack thereof)?
if params[:assignee_id].present? if params[:assignee_id].present?
......
...@@ -38,6 +38,8 @@ module Projects ...@@ -38,6 +38,8 @@ module Projects
if @project.valid? && @project.import_url.present? if @project.valid? && @project.import_url.present?
shell = Gitlab::Shell.new shell = Gitlab::Shell.new
if shell.import_repository(@project.path_with_namespace, @project.import_url) if shell.import_repository(@project.path_with_namespace, @project.import_url)
# We should create satellite for imported repo
@project.satellite.create unless @project.satellite.exists?
true true
else else
@project.errors.add(:import_url, 'cannot clone repo') @project.errors.add(:import_url, 'cannot clone repo')
......
...@@ -5,7 +5,7 @@ class DashboardController < ApplicationController ...@@ -5,7 +5,7 @@ class DashboardController < ApplicationController
before_filter :event_filter, only: :show before_filter :event_filter, only: :show
def show def show
@groups = current_user.authorized_groups @groups = current_user.authorized_groups.sort_by(&:human_name)
@has_authorized_projects = @projects.count > 0 @has_authorized_projects = @projects.count > 0
@teams = current_user.authorized_teams @teams = current_user.authorized_teams
@projects_count = @projects.count @projects_count = @projects.count
......
class FilesController < ApplicationController
def download
note = Note.find(params[:id])
if can?(current_user, :read_project, note.project)
uploader = note.attachment
send_file uploader.file.path, disposition: 'attachment'
else
not_found!
end
end
end
...@@ -73,14 +73,14 @@ class MergeRequestsController < ProjectResourceController ...@@ -73,14 +73,14 @@ class MergeRequestsController < ProjectResourceController
if @merge_request.unchecked? if @merge_request.unchecked?
@merge_request.check_if_can_be_merged @merge_request.check_if_can_be_merged
end end
render json: {state: @merge_request.human_state} render json: {merge_status: @merge_request.human_merge_status}
rescue Gitlab::SatelliteNotExistError rescue Gitlab::SatelliteNotExistError
render json: {state: :no_satellite} render json: {merge_status: :no_satellite}
end end
def automerge def automerge
return access_denied! unless can?(current_user, :accept_mr, @project) return access_denied! unless can?(current_user, :accept_mr, @project)
if @merge_request.open? && @merge_request.can_be_merged? if @merge_request.opened? && @merge_request.can_be_merged?
@merge_request.should_remove_source_branch = params[:should_remove_source_branch] @merge_request.should_remove_source_branch = params[:should_remove_source_branch]
@merge_request.automerge!(current_user) @merge_request.automerge!(current_user)
@status = true @status = true
......
...@@ -12,7 +12,7 @@ class MilestonesController < ProjectResourceController ...@@ -12,7 +12,7 @@ class MilestonesController < ProjectResourceController
def index def index
@milestones = case params[:f] @milestones = case params[:f]
when 'all'; @project.milestones.order("closed, due_date DESC") when 'all'; @project.milestones.order("state, due_date DESC")
when 'closed'; @project.milestones.closed.order("due_date DESC") when 'closed'; @project.milestones.closed.order("due_date DESC")
else @project.milestones.active.order("due_date ASC") else @project.milestones.active.order("due_date ASC")
end end
......
...@@ -51,7 +51,9 @@ class ProfilesController < ApplicationController ...@@ -51,7 +51,9 @@ class ProfilesController < ApplicationController
end end
def update_username def update_username
if @user.can_change_username?
@user.update_attributes(username: params[:user][:username]) @user.update_attributes(username: params[:user][:username])
end
respond_to do |format| respond_to do |format|
format.js format.js
......
...@@ -4,7 +4,11 @@ class TeamMembersController < ProjectResourceController ...@@ -4,7 +4,11 @@ class TeamMembersController < ProjectResourceController
before_filter :authorize_admin_project!, except: [:index, :show] before_filter :authorize_admin_project!, except: [:index, :show]
def index def index
@teams = UserTeam.scoped @team = @project.users_projects.scoped
@team = @team.send(params[:type]) if %w(masters developers reporters guests).include?(params[:type])
@team = @team.sort_by(&:project_access).reverse.group_by(&:project_access)
@assigned_teams = @project.user_team_project_relationships
end end
def show def show
......
...@@ -27,7 +27,13 @@ class Teams::MembersController < Teams::ApplicationController ...@@ -27,7 +27,13 @@ class Teams::MembersController < Teams::ApplicationController
end end
def update def update
options = {default_projects_access: params[:default_project_access], group_admin: params[:group_admin]} member_params = params[:team_member]
options = {
default_projects_access: member_params[:permission],
group_admin: member_params[:group_admin]
}
if user_team.update_membership(team_member, options) if user_team.update_membership(team_member, options)
redirect_to team_members_path(user_team), notice: "Membership for #{team_member.name} was successfully updated in Team of users." redirect_to team_members_path(user_team), notice: "Membership for #{team_member.name} was successfully updated in Team of users."
else else
...@@ -45,5 +51,4 @@ class Teams::MembersController < Teams::ApplicationController ...@@ -45,5 +51,4 @@ class Teams::MembersController < Teams::ApplicationController
def team_member def team_member
@member ||= user_team.members.find_by_username(params[:id]) @member ||= user_team.members.find_by_username(params[:id])
end end
end end
...@@ -9,13 +9,11 @@ class TeamsController < ApplicationController ...@@ -9,13 +9,11 @@ class TeamsController < ApplicationController
layout 'user_team', except: [:new, :create] layout 'user_team', except: [:new, :create]
def show def show
user_team
projects projects
@events = Event.in_projects(user_team.project_ids).limit(20).offset(params[:offset] || 0) @events = Event.in_projects(user_team.project_ids).limit(20).offset(params[:offset] || 0)
end end
def edit def edit
user_team
end end
def update def update
...@@ -41,6 +39,9 @@ class TeamsController < ApplicationController ...@@ -41,6 +39,9 @@ class TeamsController < ApplicationController
@team.path = @team.name.dup.parameterize if @team.name @team.path = @team.name.dup.parameterize if @team.name
if @team.save if @team.save
# Add current user as Master to the team
@team.add_members([current_user.id], UsersProject::MASTER, true)
redirect_to team_path(@team) redirect_to team_path(@team)
else else
render action: :new render action: :new
......
...@@ -73,8 +73,8 @@ module ApplicationHelper ...@@ -73,8 +73,8 @@ module ApplicationHelper
def search_autocomplete_source def search_autocomplete_source
projects = current_user.authorized_projects.map { |p| { label: "project: #{p.name_with_namespace}", url: project_path(p) } } projects = current_user.authorized_projects.map { |p| { label: "project: #{p.name_with_namespace}", url: project_path(p) } }
groups = current_user.authorized_groups.map { |group| { label: "group: #{group.name}", url: group_path(group) } } groups = current_user.authorized_groups.map { |group| { label: "group: #{simple_sanitize(group.name)}", url: group_path(group) } }
teams = current_user.authorized_teams.map { |team| { label: "team: #{team.name}", url: team_path(team) } } teams = current_user.authorized_teams.map { |team| { label: "team: #{simple_sanitize(team.name)}", url: team_path(team) } }
default_nav = [ default_nav = [
{ label: "My Profile", url: profile_path }, { label: "My Profile", url: profile_path },
...@@ -159,8 +159,13 @@ module ApplicationHelper ...@@ -159,8 +159,13 @@ module ApplicationHelper
alt: "Sign in with #{provider.to_s.titleize}") alt: "Sign in with #{provider.to_s.titleize}")
end end
def simple_sanitize str
sanitize(str, tags: %w(a span))
end
def image_url(source) def image_url(source)
root_url + path_to_image(source) root_url + path_to_image(source)
end end
alias_method :url_to_image, :image_url alias_method :url_to_image, :image_url
end end
...@@ -27,6 +27,6 @@ module DashboardHelper ...@@ -27,6 +27,6 @@ module DashboardHelper
items.opened items.opened
end end
items.where(assignee_id: current_user.id).count items.cared(current_user).count
end end
end end
...@@ -6,7 +6,7 @@ module IssuesHelper ...@@ -6,7 +6,7 @@ module IssuesHelper
def issue_css_classes issue def issue_css_classes issue
classes = "issue" classes = "issue"
classes << " closed" if issue.closed classes << " closed" if issue.closed?
classes << " today" if issue.today? classes << " today" if issue.today?
classes classes
end end
......
...@@ -12,7 +12,7 @@ module MergeRequestsHelper ...@@ -12,7 +12,7 @@ module MergeRequestsHelper
def mr_css_classes mr def mr_css_classes mr
classes = "merge_request" classes = "merge_request"
classes << " closed" if mr.closed classes << " closed" if mr.closed?
classes << " merged" if mr.merged? classes << " merged" if mr.merged?
classes classes
end end
......
...@@ -10,8 +10,8 @@ module NamespacesHelper ...@@ -10,8 +10,8 @@ module NamespacesHelper
global_opts = ["Global", [['/', Namespace.global_id]] ] global_opts = ["Global", [['/', Namespace.global_id]] ]
group_opts = ["Groups", groups.map {|g| [g.human_name, g.id]} ] group_opts = ["Groups", groups.sort_by(&:human_name).map {|g| [g.human_name, g.id]} ]
users_opts = [ "Users", users.map {|u| [u.human_name, u.id]} ] users_opts = [ "Users", users.sort_by(&:human_name).map {|u| [u.human_name, u.id]} ]
options = [] options = []
options << global_opts if current_user.admin options << global_opts if current_user.admin
......
module ProjectsHelper module ProjectsHelper
def grouper_project_members(project)
@project.users_projects.sort_by(&:project_access).reverse.group_by(&:project_access)
end
def grouper_project_teams(project)
@project.user_team_project_relationships.sort_by(&:greatest_access).reverse.group_by(&:greatest_access)
end
def remove_from_project_team_message(project, user) def remove_from_project_team_message(project, user)
"You are going to remove #{user.name} from #{project.name} project team. Are you sure?" "You are going to remove #{user.name} from #{project.name} project team. Are you sure?"
end end
...@@ -56,7 +48,7 @@ module ProjectsHelper ...@@ -56,7 +48,7 @@ module ProjectsHelper
def project_title project def project_title project
if project.group if project.group
content_tag :span do content_tag :span do
link_to(project.group.name, group_path(project.group)) + " / " + project.name link_to(simple_sanitize(project.group.name), group_path(project.group)) + " / " + project.name
end end
else else
project.name project.name
......
...@@ -123,7 +123,7 @@ class Ability ...@@ -123,7 +123,7 @@ class Ability
def user_team_abilities user, team def user_team_abilities user, team
rules = [] rules = []
# Only group owner and administrators can manage group # Only group owner and administrators can manage team
if team.owner == user || team.admin?(user) || user.admin? if team.owner == user || team.admin?(user) || user.admin?
rules << [ :manage_user_team ] rules << [ :manage_user_team ]
end end
......
...@@ -17,10 +17,9 @@ module Issuable ...@@ -17,10 +17,9 @@ module Issuable
validates :project, presence: true validates :project, presence: true
validates :author, presence: true validates :author, presence: true
validates :title, presence: true, length: { within: 0..255 } validates :title, presence: true, length: { within: 0..255 }
validates :closed, inclusion: { in: [true, false] }
scope :opened, -> { where(closed: false) } scope :opened, -> { with_state(:opened) }
scope :closed, -> { where(closed: true) } scope :closed, -> { with_state(:closed) }
scope :of_group, ->(group) { where(project_id: group.project_ids) } 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) } scope :of_user_team, ->(team) { where(project_id: team.project_ids, assignee_id: team.member_ids) }
scope :assigned, ->(u) { where(assignee_id: u.id)} scope :assigned, ->(u) { where(assignee_id: u.id)}
...@@ -62,14 +61,6 @@ module Issuable ...@@ -62,14 +61,6 @@ module Issuable
assignee_id_changed? assignee_id_changed?
end end
def is_being_closed?
closed_changed? && closed
end
def is_being_reopened?
closed_changed? && !closed
end
# #
# Votes # Votes
# #
......
...@@ -130,10 +130,6 @@ class Event < ActiveRecord::Base ...@@ -130,10 +130,6 @@ class Event < ActiveRecord::Base
target if target_type == "MergeRequest" target if target_type == "MergeRequest"
end end
def author
@author ||= User.find(author_id)
end
def action_name def action_name
if closed? if closed?
"closed" "closed"
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
# project_id :integer # project_id :integer
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
# closed :boolean default(FALSE), not null # state :string default(FALSE), not null
# position :integer default(0) # position :integer default(0)
# branch_name :string(255) # branch_name :string(255)
# description :text # description :text
...@@ -19,12 +19,35 @@ ...@@ -19,12 +19,35 @@
class Issue < ActiveRecord::Base class Issue < ActiveRecord::Base
include Issuable include Issuable
attr_accessible :title, :assignee_id, :closed, :position, :description, attr_accessible :title, :assignee_id, :position, :description,
:milestone_id, :label_list, :author_id_of_changes :milestone_id, :label_list, :author_id_of_changes,
:state_event
acts_as_taggable_on :labels acts_as_taggable_on :labels
def self.open_for(user) class << self
def cared(user)
where('assignee_id = :user', user: user.id)
end
def open_for(user)
opened.assigned(user) opened.assigned(user)
end end
end
state_machine :state, initial: :opened do
event :close do
transition [:reopened, :opened] => :closed
end
event :reopen do
transition closed: :reopened
end
state :opened
state :reopened
state :closed
end
end end
...@@ -35,7 +35,7 @@ class Key < ActiveRecord::Base ...@@ -35,7 +35,7 @@ class Key < ActiveRecord::Base
def fingerprintable_key def fingerprintable_key
return true unless key # Don't test if there is no key. return true unless key # Don't test if there is no key.
# `ssh-keygen -lf /dev/stdin <<< "#{key}"` errors with: redirection unexpected
file = Tempfile.new('key_file') file = Tempfile.new('key_file')
begin begin
file.puts key file.puts key
...@@ -45,7 +45,7 @@ class Key < ActiveRecord::Base ...@@ -45,7 +45,7 @@ class Key < ActiveRecord::Base
file.close file.close
file.unlink # deletes the temp file file.unlink # deletes the temp file
end end
errors.add(:key, "can't be fingerprinted") if fingerprint_output.match("failed") errors.add(:key, "can't be fingerprinted") if $?.exitstatus != 0
end end
def set_identifier def set_identifier
......
...@@ -9,15 +9,14 @@ ...@@ -9,15 +9,14 @@
# author_id :integer # author_id :integer
# assignee_id :integer # assignee_id :integer
# title :string(255) # title :string(255)
# closed :boolean default(FALSE), not null # state :string(255) not null
# created_at :datetime not null # created_at :datetime not null
# updated_at :datetime not null # updated_at :datetime not null
# st_commits :text(2147483647) # st_commits :text(2147483647)
# st_diffs :text(2147483647) # st_diffs :text(2147483647)
# merged :boolean default(FALSE), not null # merge_status :integer default(1), not null
# state :integer default(1), not null
# milestone_id :integer
# #
# milestone_id :integer
require Rails.root.join("app/models/commit") require Rails.root.join("app/models/commit")
require Rails.root.join("lib/static_model") require Rails.root.join("lib/static_model")
...@@ -25,11 +24,33 @@ require Rails.root.join("lib/static_model") ...@@ -25,11 +24,33 @@ require Rails.root.join("lib/static_model")
class MergeRequest < ActiveRecord::Base class MergeRequest < ActiveRecord::Base
include Issuable include Issuable
attr_accessible :title, :assignee_id, :closed, :target_branch, :source_branch, :milestone_id, attr_accessible :title, :assignee_id, :target_branch, :source_branch, :milestone_id,
:author_id_of_changes :author_id_of_changes, :state_event
attr_accessor :should_remove_source_branch attr_accessor :should_remove_source_branch
state_machine :state, initial: :opened do
event :close do
transition [:reopened, :opened] => :closed
end
event :merge do
transition [:reopened, :opened] => :merged
end
event :reopen do
transition closed: :reopened
end
state :opened
state :reopened
state :closed
state :merged
end
BROKEN_DIFF = "--broken-diff" BROKEN_DIFF = "--broken-diff"
UNCHECKED = 1 UNCHECKED = 1
...@@ -43,21 +64,33 @@ class MergeRequest < ActiveRecord::Base ...@@ -43,21 +64,33 @@ class MergeRequest < ActiveRecord::Base
validates :target_branch, presence: true validates :target_branch, presence: true
validate :validate_branches validate :validate_branches
def self.find_all_by_branch(branch_name) scope :merged, -> { with_state(:merged) }
class << self
def find_all_by_branch(branch_name)
where("source_branch LIKE :branch OR target_branch LIKE :branch", branch: branch_name) where("source_branch LIKE :branch OR target_branch LIKE :branch", branch: branch_name)
end end
def self.find_all_by_milestone(milestone) def cared(user)
where('assignee_id = :user OR author_id = :user', user: user.id)
end
def find_all_by_branch(branch_name)
where("source_branch LIKE :branch OR target_branch LIKE :branch", branch: branch_name)
end
def find_all_by_milestone(milestone)
where("milestone_id = :milestone_id", milestone_id: milestone) where("milestone_id = :milestone_id", milestone_id: milestone)
end end
end
def human_state def human_merge_status
states = { merge_statuses = {
CAN_BE_MERGED => "can_be_merged", CAN_BE_MERGED => "can_be_merged",
CANNOT_BE_MERGED => "cannot_be_merged", CANNOT_BE_MERGED => "cannot_be_merged",
UNCHECKED => "unchecked" UNCHECKED => "unchecked"
} }
states[self.state] merge_statuses[self.merge_status]
end end
def validate_branches def validate_branches
...@@ -72,20 +105,20 @@ class MergeRequest < ActiveRecord::Base ...@@ -72,20 +105,20 @@ class MergeRequest < ActiveRecord::Base
end end
def unchecked? def unchecked?
state == UNCHECKED merge_status == UNCHECKED
end end
def mark_as_unchecked def mark_as_unchecked
self.state = UNCHECKED self.merge_status = UNCHECKED
self.save self.save
end end
def can_be_merged? def can_be_merged?
state == CAN_BE_MERGED merge_status == CAN_BE_MERGED
end end
def check_if_can_be_merged def check_if_can_be_merged
self.state = if Gitlab::Satellite::MergeAction.new(self.author, self).can_be_merged? self.merge_status = if Gitlab::Satellite::MergeAction.new(self.author, self).can_be_merged?
CAN_BE_MERGED CAN_BE_MERGED
else else
CANNOT_BE_MERGED CANNOT_BE_MERGED
...@@ -98,7 +131,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -98,7 +131,7 @@ class MergeRequest < ActiveRecord::Base
end end
def reloaded_diffs def reloaded_diffs
if open? && unmerged_diffs.any? if opened? && unmerged_diffs.any?
self.st_diffs = unmerged_diffs self.st_diffs = unmerged_diffs
self.save self.save
end end
...@@ -128,10 +161,6 @@ class MergeRequest < ActiveRecord::Base ...@@ -128,10 +161,6 @@ class MergeRequest < ActiveRecord::Base
commits.first commits.first
end end
def merged?
merged && merge_event
end
def merge_event def merge_event
self.project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::MERGED).last self.project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::MERGED).last
end end
...@@ -146,26 +175,16 @@ class MergeRequest < ActiveRecord::Base ...@@ -146,26 +175,16 @@ class MergeRequest < ActiveRecord::Base
def probably_merged? def probably_merged?
unmerged_commits.empty? && unmerged_commits.empty? &&
commits.any? && open? commits.any? && opened?
end
def open?
!closed
end
def mark_as_merged!
self.merged = true
self.closed = true
save
end end
def mark_as_unmergable def mark_as_unmergable
self.state = CANNOT_BE_MERGED self.merge_status = CANNOT_BE_MERGED
self.save self.save
end end
def reloaded_commits def reloaded_commits
if open? && unmerged_commits.any? if opened? && unmerged_commits.any?
self.st_commits = unmerged_commits self.st_commits = unmerged_commits
save save
end end
...@@ -181,7 +200,8 @@ class MergeRequest < ActiveRecord::Base ...@@ -181,7 +200,8 @@ class MergeRequest < ActiveRecord::Base
end end
def merge!(user_id) def merge!(user_id)
self.mark_as_merged! self.merge
Event.create( Event.create(
project: self.project, project: self.project,
action: Event::MERGED, action: Event::MERGED,
......
...@@ -13,19 +13,32 @@ ...@@ -13,19 +13,32 @@
# #
class Milestone < ActiveRecord::Base class Milestone < ActiveRecord::Base
attr_accessible :title, :description, :due_date, :closed, :author_id_of_changes attr_accessible :title, :description, :due_date, :state_event, :author_id_of_changes
attr_accessor :author_id_of_changes attr_accessor :author_id_of_changes
belongs_to :project belongs_to :project
has_many :issues has_many :issues
has_many :merge_requests has_many :merge_requests
scope :active, -> { where(closed: false) } scope :active, -> { with_state(:active) }
scope :closed, -> { where(closed: true) } scope :closed, -> { with_state(:closed) }
validates :title, presence: true validates :title, presence: true
validates :project, presence: true validates :project, presence: true
validates :closed, inclusion: { in: [true, false] }
state_machine :state, initial: :active do
event :close do
transition active: :closed
end
event :activate do
transition closed: :active
end
state :closed
state :active
end
def expired? def expired?
if due_date if due_date
...@@ -68,17 +81,13 @@ class Milestone < ActiveRecord::Base ...@@ -68,17 +81,13 @@ class Milestone < ActiveRecord::Base
end end
def can_be_closed? def can_be_closed?
open? && issues.opened.count.zero? active? && issues.opened.count.zero?
end end
def is_empty? def is_empty?
total_items_count.zero? total_items_count.zero?
end end
def open?
!closed
end
def author_id def author_id
author_id_of_changes author_id_of_changes
end end
......
...@@ -17,11 +17,15 @@ class Namespace < ActiveRecord::Base ...@@ -17,11 +17,15 @@ class Namespace < ActiveRecord::Base
has_many :projects, dependent: :destroy has_many :projects, dependent: :destroy
belongs_to :owner, class_name: "User" belongs_to :owner, class_name: "User"
validates :name, presence: true, uniqueness: true validates :owner, presence: true
validates :name, presence: true, uniqueness: true,
length: { within: 0..255 },
format: { with: Gitlab::Regex.name_regex,
message: "only letters, digits, spaces & '_' '-' '.' allowed." }
validates :path, uniqueness: true, presence: true, length: { within: 1..255 }, validates :path, uniqueness: true, presence: true, length: { within: 1..255 },
format: { with: Gitlab::Regex.path_regex, format: { with: Gitlab::Regex.path_regex,
message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" } message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
validates :owner, presence: true
delegate :name, to: :owner, allow_nil: true, prefix: true delegate :name, to: :owner, allow_nil: true, prefix: true
......
...@@ -43,7 +43,7 @@ class Project < ActiveRecord::Base ...@@ -43,7 +43,7 @@ class Project < ActiveRecord::Base
has_many :events, dependent: :destroy has_many :events, dependent: :destroy
has_many :merge_requests, dependent: :destroy has_many :merge_requests, dependent: :destroy
has_many :issues, dependent: :destroy, order: "closed, created_at DESC" has_many :issues, dependent: :destroy, order: "state, created_at DESC"
has_many :milestones, dependent: :destroy has_many :milestones, dependent: :destroy
has_many :users_projects, dependent: :destroy has_many :users_projects, dependent: :destroy
has_many :notes, dependent: :destroy has_many :notes, dependent: :destroy
...@@ -146,7 +146,7 @@ class Project < ActiveRecord::Base ...@@ -146,7 +146,7 @@ class Project < ActiveRecord::Base
end end
def saved? def saved?
id && valid? id && persisted?
end end
def import? def import?
......
...@@ -132,16 +132,16 @@ class Repository ...@@ -132,16 +132,16 @@ class Repository
return nil unless commit return nil unless commit
# Build file path # Build file path
file_name = self.path_with_namespace + "-" + commit.id.to_s + ".tar.gz" file_name = self.path_with_namespace.gsub("/","_") + "-" + commit.id.to_s + ".tar.gz"
storage_path = Rails.root.join("tmp", "repositories") storage_path = Rails.root.join("tmp", "repositories")
file_path = File.join(storage_path, file_name) file_path = File.join(storage_path, self.path_with_namespace, file_name)
# Put files into a directory before archiving # Put files into a directory before archiving
prefix = self.path_with_namespace + "/" prefix = self.path_with_namespace + "/"
# Create file if not exists # Create file if not exists
unless File.exists?(file_path) unless File.exists?(file_path)
FileUtils.mkdir_p storage_path FileUtils.mkdir_p File.dirname(file_path)
file = self.repo.archive_to_file(ref, prefix, file_path) file = self.repo.archive_to_file(ref, prefix, file_path)
end end
......
...@@ -234,8 +234,12 @@ class User < ActiveRecord::Base ...@@ -234,8 +234,12 @@ class User < ActiveRecord::Base
keys.count == 0 keys.count == 0
end end
def can_change_username?
Gitlab.config.gitlab.username_changing_enabled
end
def can_create_project? def can_create_project?
projects_limit > personal_projects.count projects_limit > owned_projects.count
end end
def can_create_group? def can_create_group?
...@@ -263,7 +267,7 @@ class User < ActiveRecord::Base ...@@ -263,7 +267,7 @@ class User < ActiveRecord::Base
end end
def cared_merge_requests def cared_merge_requests
MergeRequest.where("author_id = :id or assignee_id = :id", id: self.id) MergeRequest.cared(self)
end end
# Remove user from all projects and # Remove user from all projects and
......
...@@ -21,8 +21,11 @@ class UserTeam < ActiveRecord::Base ...@@ -21,8 +21,11 @@ class UserTeam < ActiveRecord::Base
has_many :projects, through: :user_team_project_relationships has_many :projects, through: :user_team_project_relationships
has_many :members, through: :user_team_user_relationships, source: :user has_many :members, through: :user_team_user_relationships, source: :user
validates :name, presence: true, uniqueness: true
validates :owner, presence: true validates :owner, presence: true
validates :name, presence: true, uniqueness: true,
length: { within: 0..255 },
format: { with: Gitlab::Regex.name_regex,
message: "only letters, digits, spaces & '_' '-' '.' allowed." }
validates :path, uniqueness: true, presence: true, length: { within: 1..255 }, validates :path, uniqueness: true, presence: true, length: { within: 1..255 },
format: { with: Gitlab::Regex.path_regex, format: { with: Gitlab::Regex.path_regex,
message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" } message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
......
...@@ -26,6 +26,10 @@ class UserTeamProjectRelationship < ActiveRecord::Base ...@@ -26,6 +26,10 @@ class UserTeamProjectRelationship < ActiveRecord::Base
user_team.name user_team.name
end end
def human_max_access
UserTeam.access_roles.key(greatest_access)
end
private private
def check_greatest_access def check_greatest_access
......
...@@ -20,15 +20,23 @@ class ActivityObserver < ActiveRecord::Observer ...@@ -20,15 +20,23 @@ class ActivityObserver < ActiveRecord::Observer
end end
end end
def after_save(record) def after_close(record, transition)
if record.changed.include?("closed") && record.author_id_of_changes
Event.create( Event.create(
project: record.project, project: record.project,
target_id: record.id, target_id: record.id,
target_type: record.class.name, target_type: record.class.name,
action: (record.closed ? Event::CLOSED : Event::REOPENED), action: Event::CLOSED,
author_id: record.author_id_of_changes author_id: record.author_id_of_changes
) )
end end
def after_reopen(record, transition)
Event.create(
project: record.project,
target_id: record.id,
target_type: record.class.name,
action: Event::REOPENED,
author_id: record.author_id_of_changes
)
end end
end end
...@@ -7,22 +7,31 @@ class IssueObserver < ActiveRecord::Observer ...@@ -7,22 +7,31 @@ class IssueObserver < ActiveRecord::Observer
end end
end end
def after_update(issue) def after_close(issue, transition)
send_reassigned_email(issue) if issue.is_being_reassigned? send_reassigned_email(issue) if issue.is_being_reassigned?
status = nil create_note(issue)
status = 'closed' if issue.is_being_closed?
status = 'reopened' if issue.is_being_reopened?
if status
Note.create_status_change_note(issue, current_user, status)
[issue.author, issue.assignee].compact.each do |recipient|
Notify.delay.issue_status_changed_email(recipient.id, issue.id, status, current_user.id)
end end
def after_reopen(issue, transition)
send_reassigned_email(issue) if issue.is_being_reassigned?
create_note(issue)
end end
def after_update(issue)
send_reassigned_email(issue) if issue.is_being_reassigned?
end end
protected protected
def create_note(issue)
Note.create_status_change_note(issue, current_user, issue.state)
[issue.author, issue.assignee].compact.each do |recipient|
Notify.delay.issue_status_changed_email(recipient.id, issue.id, issue.state, current_user.id)
end
end
def send_reassigned_email(issue) def send_reassigned_email(issue)
recipient_ids = [issue.assignee_id, issue.assignee_id_was].keep_if {|id| id && id != current_user.id } recipient_ids = [issue.assignee_id, issue.assignee_id_was].keep_if {|id| id && id != current_user.id }
......
...@@ -7,15 +7,20 @@ class MergeRequestObserver < ActiveRecord::Observer ...@@ -7,15 +7,20 @@ class MergeRequestObserver < ActiveRecord::Observer
end end
end end
def after_update(merge_request) def after_close(merge_request, transition)
send_reassigned_email(merge_request) if merge_request.is_being_reassigned?
Note.create_status_change_note(merge_request, current_user, merge_request.state)
end
def after_reopen(merge_request, transition)
send_reassigned_email(merge_request) if merge_request.is_being_reassigned? send_reassigned_email(merge_request) if merge_request.is_being_reassigned?
status = nil Note.create_status_change_note(merge_request, current_user, merge_request.state)
status = 'closed' if merge_request.is_being_closed?
status = 'reopened' if merge_request.is_being_reopened?
if status
Note.create_status_change_note(merge_request, current_user, status)
end end
def after_update(merge_request)
send_reassigned_email(merge_request) if merge_request.is_being_reassigned?
end end
protected protected
......
...@@ -19,4 +19,12 @@ class AttachmentUploader < CarrierWave::Uploader::Base ...@@ -19,4 +19,12 @@ class AttachmentUploader < CarrierWave::Uploader::Base
rescue rescue
false false
end end
def secure_url
if self.class.storage == CarrierWave::Storage::File
"/files/#{model.class.to_s.underscore}/#{model.id}/#{file.filename}"
else
url
end
end
end end
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
%td= group.path %td= group.path
%td= group.projects.count %td= group.projects.count
%td %td
= link_to group.owner_name, admin_user_path(group.owner_id) = link_to group.owner_name, admin_user_path(group.owner)
%td.bgred %td.bgred
= link_to 'Rename', edit_admin_group_path(group), id: "edit_#{dom_id(group)}", class: "btn btn-small" = link_to 'Rename', edit_admin_group_path(group), id: "edit_#{dom_id(group)}", class: "btn btn-small"
= link_to 'Destroy', [:admin, group], confirm: "REMOVE #{group.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove" = link_to 'Destroy', [:admin, group], confirm: "REMOVE #{group.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove"
......
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
%td= team.projects.count %td= team.projects.count
%td= team.members.count %td= team.members.count
%td %td
= link_to team.owner.name, admin_user_path(team.owner_id) = link_to team.owner.name, admin_user_path(team.owner)
%td.bgred %td.bgred
= link_to 'Rename', edit_admin_team_path(team), id: "edit_#{dom_id(team)}", class: "btn btn-small" = link_to 'Rename', edit_admin_team_path(team), id: "edit_#{dom_id(team)}", class: "btn btn-small"
= link_to 'Destroy', admin_team_path(team), confirm: "REMOVE #{team.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove" = link_to 'Destroy', admin_team_path(team), confirm: "REMOVE #{team.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove"
......
...@@ -6,9 +6,9 @@ ...@@ -6,9 +6,9 @@
= link_to commit.short_id(8), project_commit_path(@project, commit), class: "commit_short_id" = link_to commit.short_id(8), project_commit_path(@project, commit), class: "commit_short_id"
= commit.author_link avatar: true, size: 24 = commit.author_link avatar: true, size: 24
&nbsp; &nbsp;
= link_to_gfm truncate(commit.title, length: 50), project_commit_path(@project, commit.id), class: "row_title" = link_to_gfm truncate(commit.title, length: 70), project_commit_path(@project, commit.id), class: "row_title"
%span.committed_ago %time.committed_ago{ datetime: commit.committed_date, title: commit.committed_date.stamp("Aug 21, 2011 9:23pm") }
= time_ago_in_words(commit.committed_date) = time_ago_in_words(commit.committed_date)
ago ago
&nbsp; &nbsp;
......
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
= markdown truncate(event.target.note, length: 70) = markdown truncate(event.target.note, length: 70)
- note = event.target - note = event.target
- if note.attachment.url - if note.attachment.url
= link_to note.attachment.url, target: "_blank", class: 'note-file-attach' do = link_to note.attachment.secure_url, target: "_blank", class: 'note-file-attach' do
- if note.attachment.image? - if note.attachment.image?
= image_tag note.attachment.url, class: 'note-image-attach' = image_tag note.attachment.url, class: 'note-image-attach'
- else - else
......
...@@ -26,6 +26,8 @@ ...@@ -26,6 +26,8 @@
= link_to group_filter_path(entity, project_id: project.id) do = link_to group_filter_path(entity, project_id: project.id) do
= project.name_with_namespace = project.name_with_namespace
%small.pull-right= entities_per_project(project, entity) %small.pull-right= entities_per_project(project, entity)
- if @projects.blank?
%p.nothing_here_message This group has no projects yet
%fieldset %fieldset
%hr %hr
......
...@@ -7,6 +7,8 @@ ...@@ -7,6 +7,8 @@
= link_to people_group_path(@group, project_id: project.id) do = link_to people_group_path(@group, project_id: project.id) do
= project.name_with_namespace = project.name_with_namespace
%small.pull-right= project.users.count %small.pull-right= project.users.count
- if @projects.blank?
%p.nothing_here_message This group has no projects yet
%fieldset %fieldset
%hr %hr
......
...@@ -30,6 +30,8 @@ ...@@ -30,6 +30,8 @@
= link_to 'Team', project_team_index_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small" = link_to 'Team', project_team_index_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small"
= link_to 'Edit', edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small" = link_to 'Edit', edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small"
= link_to 'Remove', project, confirm: "REMOVE #{project.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove" = link_to 'Remove', project, confirm: "REMOVE #{project.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove"
- if @group.projects.blank?
%p.nothing_here_message This group has no projects yet
.span5 .span5
.ui-box .ui-box
......
%h3.page_title %h3.page_title
GITLAB GITLAB
.pull-right .pull-right
%span= Gitlab::Version %span= Gitlab::VERSION
%small= Gitlab::Revision %small= Gitlab::REVISION
%hr %hr
%p.lead %p.lead
Self Hosted Git Management Self Hosted Git Management
......
...@@ -8,10 +8,10 @@ ...@@ -8,10 +8,10 @@
%i.icon-comment %i.icon-comment
= issue.notes.count = issue.notes.count
- if can? current_user, :modify_issue, issue - if can? current_user, :modify_issue, issue
- if issue.closed - if issue.closed?
= link_to 'Reopen', project_issue_path(issue.project, issue, issue: {closed: false }, status_only: true), method: :put, class: "btn btn-small grouped reopen_issue", remote: true = link_to 'Reopen', project_issue_path(issue.project, issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn btn-small grouped reopen_issue", remote: true
- else - else
= link_to 'Close', project_issue_path(issue.project, issue, issue: {closed: true }, status_only: true), method: :put, class: "btn btn-small grouped close_issue", remote: true = link_to 'Close', project_issue_path(issue.project, issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn btn-small grouped close_issue", remote: true
= link_to edit_project_issue_path(issue.project, issue), class: "btn btn-small edit-issue-link grouped" do = link_to edit_project_issue_path(issue.project, issue), class: "btn btn-small edit-issue-link grouped" do
%i.icon-edit %i.icon-edit
Edit Edit
......
...@@ -7,10 +7,10 @@ ...@@ -7,10 +7,10 @@
%span.pull-right %span.pull-right
- if can?(current_user, :admin_project, @project) || @issue.author == current_user - if can?(current_user, :admin_project, @project) || @issue.author == current_user
- if @issue.closed - if @issue.closed?
= link_to 'Reopen', project_issue_path(@project, @issue, issue: {closed: false }, status_only: true), method: :put, class: "btn grouped reopen_issue" = link_to 'Reopen', project_issue_path(@project, @issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn grouped reopen_issue"
- else - else
= link_to 'Close', project_issue_path(@project, @issue, issue: {closed: true }, status_only: true), method: :put, class: "btn grouped close_issue", title: "Close Issue" = link_to 'Close', project_issue_path(@project, @issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn grouped close_issue", title: "Close Issue"
- if can?(current_user, :admin_project, @project) || @issue.author == current_user - if can?(current_user, :admin_project, @project) || @issue.author == current_user
= link_to edit_project_issue_path(@project, @issue), class: "btn grouped" do = link_to edit_project_issue_path(@project, @issue), class: "btn grouped" do
%i.icon-edit %i.icon-edit
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
.ui-box.ui-box-show .ui-box.ui-box-show
.ui-box-head .ui-box-head
%h4.box-title %h4.box-title
- if @issue.closed - if @issue.closed?
.error.status_info Closed .error.status_info Closed
= gfm escape_once(@issue.title) = gfm escape_once(@issue.title)
......
...@@ -29,10 +29,10 @@ ...@@ -29,10 +29,10 @@
$(function(){ $(function(){
merge_request = new MergeRequest({ merge_request = new MergeRequest({
url_to_automerge_check: "#{automerge_check_project_merge_request_path(@project, @merge_request)}", url_to_automerge_check: "#{automerge_check_project_merge_request_path(@project, @merge_request)}",
check_enable: #{@merge_request.state == MergeRequest::UNCHECKED ? "true" : "false"}, check_enable: #{@merge_request.merge_status == MergeRequest::UNCHECKED ? "true" : "false"},
url_to_ci_check: "#{ci_status_project_merge_request_path(@project, @merge_request)}", url_to_ci_check: "#{ci_status_project_merge_request_path(@project, @merge_request)}",
ci_enable: #{@project.gitlab_ci? ? "true" : "false"}, ci_enable: #{@project.gitlab_ci? ? "true" : "false"},
current_state: "#{@merge_request.human_state}", current_status: "#{@merge_request.human_merge_status}",
action: "#{controller.action_name}" action: "#{controller.action_name}"
}); });
}); });
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
%strong Only masters can accept MR %strong Only masters can accept MR
- if @merge_request.open? && @commits.any? && can?(current_user, :accept_mr, @project) - if @merge_request.opened? && @commits.any? && can?(current_user, :accept_mr, @project)
.automerge_widget.can_be_merged{style: "display:none"} .automerge_widget.can_be_merged{style: "display:none"}
.alert.alert-success .alert.alert-success
%span %span
......
.ui-box.ui-box-show .ui-box.ui-box-show
.ui-box-head .ui-box-head
%h4.box-title %h4.box-title
- if @merge_request.merged - if @merge_request.merged?
.error.status_info .error.status_info
%i.icon-ok %i.icon-ok
Merged Merged
- elsif @merge_request.closed - elsif @merge_request.closed?
.error.status_info Closed .error.status_info Closed
= gfm escape_once(@merge_request.title) = gfm escape_once(@merge_request.title)
...@@ -21,14 +21,14 @@ ...@@ -21,14 +21,14 @@
%strong= link_to_gfm truncate(milestone.title, length: 20), project_milestone_path(milestone.project, milestone) %strong= link_to_gfm truncate(milestone.title, length: 20), project_milestone_path(milestone.project, milestone)
- if @merge_request.closed - if @merge_request.closed?
.ui-box-bottom .ui-box-bottom
%span
Closed by #{link_to_member(@project, @merge_request.closed_event.author)}
%small #{time_ago_in_words(@merge_request.closed_event.created_at)} ago.
- if @merge_request.merged? - if @merge_request.merged?
.ui-box-bottom
%span %span
Merged by #{link_to_member(@project, @merge_request.merge_event.author)} Merged by #{link_to_member(@project, @merge_request.merge_event.author)}
%small #{time_ago_in_words(@merge_request.merge_event.created_at)} ago. %small #{time_ago_in_words(@merge_request.merge_event.created_at)} ago.
- elsif @merge_request.closed_event
%span
Closed by #{link_to_member(@project, @merge_request.closed_event.author)}
%small #{time_ago_in_words(@merge_request.closed_event.created_at)} ago.
- if @merge_request.open? && @commits.any? - if @merge_request.opened? && @commits.any?
.ci_widget.ci-success{style: "display:none"} .ci_widget.ci-success{style: "display:none"}
.alert.alert-success .alert.alert-success
%i.icon-ok %i.icon-ok
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
%span.pull-right %span.pull-right
- if can?(current_user, :modify_merge_request, @merge_request) - if can?(current_user, :modify_merge_request, @merge_request)
- if @merge_request.open? - if @merge_request.opened?
.left.btn-group .left.btn-group
%a.btn.grouped.dropdown-toggle{ data: {toggle: :dropdown} } %a.btn.grouped.dropdown-toggle{ data: {toggle: :dropdown} }
%i.icon-download-alt %i.icon-download-alt
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
%li= link_to "Email Patches", project_merge_request_path(@project, @merge_request, format: :patch) %li= link_to "Email Patches", project_merge_request_path(@project, @merge_request, format: :patch)
%li= link_to "Plain Diff", project_merge_request_path(@project, @merge_request, format: :diff) %li= link_to "Plain Diff", project_merge_request_path(@project, @merge_request, format: :diff)
= link_to 'Close', project_merge_request_path(@project, @merge_request, merge_request: {closed: true }, status_only: true), method: :put, class: "btn grouped btn-close", title: "Close merge request" = link_to 'Close', project_merge_request_path(@project, @merge_request, merge_request: {state_event: :close }), method: :put, class: "btn grouped btn-close", title: "Close merge request"
= link_to edit_project_merge_request_path(@project, @merge_request), class: "btn grouped" do = link_to edit_project_merge_request_path(@project, @merge_request), class: "btn grouped" do
%i.icon-edit %i.icon-edit
......
%li{class: "milestone milestone-#{milestone.closed ? 'closed' : 'open'}", id: dom_id(milestone) } %li{class: "milestone milestone-#{milestone.closed? ? 'closed' : 'open'}", id: dom_id(milestone) }
.pull-right .pull-right
- if can?(current_user, :admin_milestone, milestone.project) and milestone.open? - if can?(current_user, :admin_milestone, milestone.project) and milestone.active?
= link_to edit_project_milestone_path(milestone.project, milestone), class: "btn btn-small edit-milestone-link grouped" do = link_to edit_project_milestone_path(milestone.project, milestone), class: "btn btn-small edit-milestone-link grouped" do
%i.icon-edit %i.icon-edit
Edit Edit
%h4 %h4
= link_to_gfm truncate(milestone.title, length: 100), project_milestone_path(milestone.project, milestone) = link_to_gfm truncate(milestone.title, length: 100), project_milestone_path(milestone.project, milestone)
- if milestone.expired? and not milestone.closed - if milestone.expired? and not milestone.closed?
%span.cred (Expired) %span.cred (Expired)
%small %small
= milestone.expires_at = milestone.expires_at
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
&larr; To milestones list &larr; To milestones list
.span6 .span6
.pull-right .pull-right
- unless @milestone.closed - unless @milestone.closed?
= link_to new_project_issue_path(@project, issue: { milestone_id: @milestone.id }), class: "btn btn-small grouped", title: "New Issue" do = link_to new_project_issue_path(@project, issue: { milestone_id: @milestone.id }), class: "btn btn-small grouped", title: "New Issue" do
%i.icon-plus %i.icon-plus
New Issue New Issue
...@@ -25,12 +25,12 @@ ...@@ -25,12 +25,12 @@
%hr %hr
%p %p
%span All issues for this milestone are closed. You may close milestone now. %span All issues for this milestone are closed. You may close milestone now.
= link_to 'Close Milestone', project_milestone_path(@project, @milestone, milestone: {closed: true }), method: :put, class: "btn btn-small btn-remove" = link_to 'Close Milestone', project_milestone_path(@project, @milestone, milestone: {state_event: :close }), method: :put, class: "btn btn-small btn-remove"
.ui-box.ui-box-show .ui-box.ui-box-show
.ui-box-head .ui-box-head
%h4.box-title %h4.box-title
- if @milestone.closed - if @milestone.closed?
.error.status_info Closed .error.status_info Closed
- elsif @milestone.expired? - elsif @milestone.expired?
.error.status_info Expired .error.status_info Expired
...@@ -63,7 +63,7 @@ ...@@ -63,7 +63,7 @@
%li=link_to('All Issues', '#') %li=link_to('All Issues', '#')
%ul.well-list %ul.well-list
- @issues.each do |issue| - @issues.each do |issue|
%li{data: {closed: issue.closed}} %li{data: {closed: issue.closed?}}
= link_to [@project, issue] do = link_to [@project, issue] do
%span.badge.badge-info ##{issue.id} %span.badge.badge-info ##{issue.id}
&ndash; &ndash;
...@@ -77,7 +77,7 @@ ...@@ -77,7 +77,7 @@
%li=link_to('All Merge Requests', '#') %li=link_to('All Merge Requests', '#')
%ul.well-list %ul.well-list
- @merge_requests.each do |merge_request| - @merge_requests.each do |merge_request|
%li{data: {closed: merge_request.closed}} %li{data: {closed: merge_request.closed?}}
= link_to [@project, merge_request] do = link_to [@project, merge_request] do
%span.badge.badge-info ##{merge_request.id} %span.badge.badge-info ##{merge_request.id}
&ndash; &ndash;
......
...@@ -31,7 +31,7 @@ ...@@ -31,7 +31,7 @@
- if note.attachment.image? - if note.attachment.image?
= image_tag note.attachment.url, class: 'note-image-attach' = image_tag note.attachment.url, class: 'note-image-attach'
.attachment.pull-right .attachment.pull-right
= link_to note.attachment.url, target: "_blank" do = link_to note.attachment.secure_url, target: "_blank" do
%i.icon-paper-clip %i.icon-paper-clip
= note.attachment_identifier = note.attachment_identifier
.clear .clear
...@@ -53,7 +53,8 @@ ...@@ -53,7 +53,8 @@
%fieldset.update-username - if current_user.can_change_username?
%fieldset.update-username
%legend %legend
Username Username
%small.cred.pull-right %small.cred.pull-right
......
...@@ -77,7 +77,7 @@ ...@@ -77,7 +77,7 @@
%legend %legend
Personal projects: Personal projects:
%small.pull-right %small.pull-right
%span= current_user.personal_projects.count %span= current_user.owned_projects.count
of of
%span= current_user.projects_limit %span= current_user.projects_limit
.padded .padded
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
.input .input
= f.text_field :import_url, class: 'xlarge', placeholder: 'https://github.com/randx/six.git' = f.text_field :import_url, class: 'xlarge', placeholder: 'https://github.com/randx/six.git'
.light .light
URL should be clonable URL must be clonable
%p.padded %p.padded
New projects are private by default. You choose who can see the project and commit to repository. New projects are private by default. You choose who can see the project and commit to repository.
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
- @notes.each do |note| - @notes.each do |note|
%tr %tr
%td %td
%a{href: note.attachment.url} = link_to note.attachment.secure_url, target: "_blank" do
= image_tag gravatar_icon(note.author_email), class: "avatar s24" = image_tag gravatar_icon(note.author_email), class: "avatar s24"
= note.attachment_identifier = note.attachment_identifier
%td %td
......
%li{id: dom_id(team), class: "user_team_row team_#{team.id}"}
.pull-right
- if can?(current_user, :admin_team_member, @project)
= link_to resign_project_team_path(@project, team), method: :delete, confirm: "Are you shure?", class: "btn btn-remove btn-tiny" do
%i.icon-minus.icon-white
%strong= link_to team.name, team_path(team), title: team.name, class: "dark"
%br
%small.cgray Members: #{team.members.count}
%small.cgray Max access: #{team_relation.human_max_access}
.ui-box
%ul.well-list
- assigned_teams.sort_by(&:team_name).each do |team_relation|
= render "team_members/assigned_team", team_relation: team_relation, team: team_relation.user_team
- team = team_rel.user_team
- allow_admin = can? current_user, :admin_team_member, @project
%li{id: dom_id(team), class: "user_team_row team_#{team.id}"}
.row
.span6
%strong= link_to team.name, team_path(team), title: team.name, class: "dark"
%br
%small.cgray Members: #{team.members.count}
.span5.pull-right
.pull-right
- if allow_admin
.left
= link_to resign_project_team_path(@project, team), method: :delete, confirm: "Are you shure?", class: "btn btn-remove small" do
%i.icon-minus.icon-white
- grouper_project_members(@project).each do |access, members| - team.each do |access, members|
.ui-box .ui-box
%h5.title %h5.title
= Project.access_options.key(access).pluralize = Project.access_options.key(access).pluralize
%small= members.size %small= members.size
%ul.well-list %ul.well-list
- members.sort_by(&:user_name).each do |up| - members.sort_by(&:user_name).each do |team_member|
= render(partial: 'team_members/show', locals: {member: up}) = render 'team_members/team_member', member: team_member
:javascript
$(function(){
$('.repo-access-select, .project-access-select').live("change", function() {
$(this.form).submit();
});
})
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
- allow_admin = can? current_user, :admin_project, @project - allow_admin = can? current_user, :admin_project, @project
%li{id: dom_id(user), class: "team_member_row user_#{user.id}"} %li{id: dom_id(user), class: "team_member_row user_#{user.id}"}
.row .row
.span6 .span4
= link_to project_team_member_path(@project, user), title: user.name, class: "dark" do = link_to project_team_member_path(@project, user), title: user.name, class: "dark" do
= image_tag gravatar_icon(user.email, 40), class: "avatar s32" = image_tag gravatar_icon(user.email, 40), class: "avatar s32"
= link_to project_team_member_path(@project, user), title: user.name, class: "dark" do = link_to project_team_member_path(@project, user), title: user.name, class: "dark" do
...@@ -10,18 +10,18 @@ ...@@ -10,18 +10,18 @@
%br %br
%small.cgray= user.email %small.cgray= user.email
.span5.pull-right .span4.pull-right
- if allow_admin - if allow_admin
.left .left
= form_for(member, as: :team_member, url: project_team_member_path(@project, member.user)) do |f| = form_for(member, as: :team_member, url: project_team_member_path(@project, member.user)) do |f|
= f.select :project_access, options_for_select(UsersProject.access_roles, member.project_access), {}, class: "medium project-access-select span2" = f.select :project_access, options_for_select(UsersProject.access_roles, member.project_access), {}, class: "medium project-access-select span2 trigger-submit"
.pull-right .pull-right
- if current_user == user - if current_user == user
%span.btn.disabled This is you! %span.label This is you!
- if @project.namespace_owner == user - if @project.namespace_owner == user
%span.btn.disabled Owner %span.label Owner
- elsif user.blocked - elsif user.blocked
%span.btn.disabled.blocked Blocked %span.label Blocked
- elsif allow_admin - elsif allow_admin
= link_to project_team_member_path(@project, user), confirm: remove_from_project_team_message(@project, user), method: :delete, class: "btn-tiny btn btn-remove" do = link_to project_team_member_path(@project, user), confirm: remove_from_project_team_message(@project, user), method: :delete, class: "btn-tiny btn btn-remove" do
%i.icon-minus.icon-white %i.icon-minus.icon-white
......
- grouper_project_teams(@project).each do |access, teams|
.ui-box
%h5.title
= UserTeam.access_roles.key(access).pluralize
%small= teams.size
%ul.well-list
- teams.sort_by(&:team_name).each do |tofr|
= render(partial: 'team_members/show_team', locals: {team_rel: tofr})
:javascript
$(function(){
$('.repo-access-select, .project-access-select').live("change", function() {
$(this.form).submit();
});
})
...@@ -18,16 +18,39 @@ ...@@ -18,16 +18,39 @@
%hr %hr
.clearfix .clearfix
%div.team-table .row
= render partial: "team_members/team", locals: {project: @project} .span3
%ul.nav.nav-pills.nav-stacked
%li{class: ("active" if !params[:type])}
= link_to project_team_members_path(type: nil) do
All
%li{class: ("active" if params[:type] == 'masters')}
= link_to project_team_members_path(type: 'masters') do
Masters
%span.pull-right= @project.users_projects.masters.count
%li{class: ("active" if params[:type] == 'developers')}
= link_to project_team_members_path(type: 'developers') do
Developers
%span.pull-right= @project.users_projects.developers.count
%li{class: ("active" if params[:type] == 'reporters')}
= link_to project_team_members_path(type: 'reporters') do
Reporters
%span.pull-right= @project.users_projects.reporters.count
%li{class: ("active" if params[:type] == 'guests')}
= link_to project_team_members_path(type: 'guests') do
Guests
%span.pull-right= @project.users_projects.guests.count
- if @assigned_teams.present?
%h3.page_title %h5
Assigned teams Assigned teams
(#{@project.user_teams.count}) (#{@project.user_teams.count})
%div
= render "team_members/assigned_teams", assigned_teams: @assigned_teams
.span9
%div.team-table
= render "team_members/team", team: @team
%hr
.clearfix
%div.team-table
= render partial: "team_members/teams", locals: {project: @project}
%h3.page_title= "Edit Team #{@team.name}" %h3.page_title= "Edit Team #{@team.name}"
%hr %hr
= form_for @team, url: team_path(@team) do |f| .row
.span7
= form_for @team, url: team_path(@team) do |f|
- if @team.errors.any? - if @team.errors.any?
.alert.alert-error .alert.alert-error
%span= @team.errors.full_messages.first %span= @team.errors.full_messages.first
...@@ -8,13 +10,20 @@ ...@@ -8,13 +10,20 @@
= f.label :name do = f.label :name do
Team name is Team name is
.input .input
= f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left" = f.text_field :name, placeholder: "Ex. OpenSource", class: "xlarge left"
.clearfix .clearfix
= f.label :path do = f.label :path do
Team path is Team path is
.input .input
= f.text_field :path, placeholder: "opensource", class: "xxlarge left" = f.text_field :path, placeholder: "opensource", class: "xlarge left"
.form-actions .form-actions
= f.submit 'Save team changes', class: "btn btn-primary" = f.submit 'Save team changes', class: "btn btn-save"
= link_to 'Delete team', team_path(@team), method: :delete, confirm: "You are shure?", class: "btn btn-remove pull-right" .span5
.ui-box
%h5.title Remove team
.padded.bgred
%p
Removed team can not be restored!
= link_to 'Remove team', team_path(@team), method: :delete, confirm: "You are sure?", class: "btn btn-remove btn-small"
...@@ -10,20 +10,19 @@ ...@@ -10,20 +10,19 @@
%br %br
%small.cgray= user.email %small.cgray= user.email
.span6.pull-right .span4
- if allow_admin - if allow_admin
.left.span2
= form_for(member, as: :team_member, url: team_member_path(@team, user)) do |f| = form_for(member, as: :team_member, url: team_member_path(@team, user)) do |f|
= f.select :permission, options_for_select(UsersProject.access_roles, @team.default_projects_access(user)), {}, class: "medium project-access-select span2" = f.select :permission, options_for_select(UsersProject.access_roles, @team.default_projects_access(user)), {}, class: "medium trigger-submit"
.left.span2 %br
%span = label_tag do
= check_box_tag :group_admin, true, @team.admin?(user) = f.check_box :group_admin, class: 'trigger-submit'
Admin access %span Admin access
.pull-right .pull-right
- if current_user == user - if current_user == user
%span.btn.disabled This is you! %span.btn.disabled This is you!
- if @team.owner == user - if @team.owner == user
%span.btn.disabled.btn-success Owner %span.btn.disabled Owner
- elsif user.blocked - elsif user.blocked
%span.btn.disabled.blocked Blocked %span.btn.disabled.blocked Blocked
- elsif allow_admin - elsif allow_admin
......
...@@ -17,3 +17,17 @@ ...@@ -17,3 +17,17 @@
%li All created teams are public (users can view who enter into team and which project are assigned for this team) %li All created teams are public (users can view who enter into team and which project are assigned for this team)
%li People within a team see only projects they have access to %li People within a team see only projects they have access to
%li You will be able to assign existing projects for team %li You will be able to assign existing projects for team
%hr
- if current_user.can_create_group?
.clearfix
.input.light
Need a group for several dependent projects?
= link_to new_group_path, class: "btn btn-tiny" do
Create a group
- if current_user.can_create_project?
.clearfix
.input.light
Want to create a project?
= link_to new_project_path, class: "btn btn-tiny" do
Create a project
...@@ -21,14 +21,18 @@ class PostReceive ...@@ -21,14 +21,18 @@ class PostReceive
return false return false
end end
# Ignore push from non-gitlab users user = if identifier.blank?
user = if identifier.nil? # Local push from gitlab
raise identifier.inspect
email = project.repository.commit(newrev).author.email rescue nil email = project.repository.commit(newrev).author.email rescue nil
User.find_by_email(email) if email User.find_by_email(email) if email
elsif /^[A-Z0-9._%a-z\-]+@(?:[A-Z0-9a-z\-]+\.)+[A-Za-z]{2,4}$/.match(identifier)
User.find_by_email(identifier) elsif identifier =~ /\Auser-\d+\Z/
elsif identifier =~ /key/ # git push over http
user_id = identifier.gsub("user-", "")
User.find_by_id(user_id)
elsif identifier =~ /\Akey-\d+\Z/
# git push over ssh
key_id = identifier.gsub("key-", "") key_id = identifier.gsub("key-", "")
Key.find_by_id(key_id).try(:user) Key.find_by_id(key_id).try(:user)
end end
......
...@@ -7,12 +7,13 @@ ...@@ -7,12 +7,13 @@
# 2. Replace gitlab -> host with your domain # 2. Replace gitlab -> host with your domain
# 3. Replace gitlab -> email_from # 3. Replace gitlab -> email_from
# production: &base
# 1. GitLab app settings #
# ========================== # 1. GitLab app settings
# ==========================
## GitLab settings ## GitLab settings
gitlab: gitlab:
## Web server settings ## Web server settings
host: localhost host: localhost
port: 80 port: 80
...@@ -34,21 +35,22 @@ gitlab: ...@@ -34,21 +35,22 @@ gitlab:
## Project settings ## Project settings
default_projects_limit: 10 default_projects_limit: 10
# signup_enabled: true # default: false - Account passwords are not sent via the email if signup is enabled. # signup_enabled: true # default: false - Account passwords are not sent via the email if signup is enabled.
# username_changing_enabled: false # default: true - User can change her username/namespace
## Gravatar ## Gravatar
gravatar: gravatar:
enabled: true # Use user avatar images from Gravatar.com (default: true) enabled: true # Use user avatar images from Gravatar.com (default: true)
# plain_url: "http://..." # default: http://www.gravatar.com/avatar/%{hash}?s=%{size}&d=mm # plain_url: "http://..." # default: http://www.gravatar.com/avatar/%{hash}?s=%{size}&d=mm
# ssl_url: "https://..." # default: https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=mm # ssl_url: "https://..." # default: https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=mm
# #
# 2. Auth settings # 2. Auth settings
# ========================== # ==========================
## LDAP settings ## LDAP settings
ldap: ldap:
enabled: false enabled: false
host: '_your_ldap_server' host: '_your_ldap_server'
base: '_the_base_where_you_search_for_users' base: '_the_base_where_you_search_for_users'
...@@ -58,8 +60,8 @@ ldap: ...@@ -58,8 +60,8 @@ ldap:
bind_dn: '_the_full_dn_of_the_user_you_will_bind_with' bind_dn: '_the_full_dn_of_the_user_you_will_bind_with'
password: '_the_password_of_the_bind_user' password: '_the_password_of_the_bind_user'
## Omniauth settings ## Omniauth settings
omniauth: omniauth:
# Enable ability for users # Enable ability for users
# Allow logging in via Twitter, Google, etc. using Omniauth providers # Allow logging in via Twitter, Google, etc. using Omniauth providers
enabled: false enabled: false
...@@ -88,22 +90,22 @@ omniauth: ...@@ -88,22 +90,22 @@ omniauth:
# #
# 3. Advanced settings # 3. Advanced settings
# ========================== # ==========================
# GitLab Satellites # GitLab Satellites
satellites: satellites:
# Relative paths are relative to Rails.root (default: tmp/repo_satellites/) # Relative paths are relative to Rails.root (default: tmp/repo_satellites/)
path: /home/git/gitlab-satellites/ path: /home/git/gitlab-satellites/
## Backup settings ## Backup settings
backup: backup:
path: "tmp/backups" # Relative paths are relative to Rails.root (default: tmp/backups/) path: "tmp/backups" # Relative paths are relative to Rails.root (default: tmp/backups/)
# keep_time: 604800 # default: 0 (forever) (in seconds) # keep_time: 604800 # default: 0 (forever) (in seconds)
## GitLab Shell settings ## GitLab Shell settings
gitlab_shell: gitlab_shell:
# REPOS_PATH MUST NOT BE A SYMLINK!!! # REPOS_PATH MUST NOT BE A SYMLINK!!!
repos_path: /home/git/repositories/ repos_path: /home/git/repositories/
hooks_path: /home/git/gitlab-shell/hooks/ hooks_path: /home/git/gitlab-shell/hooks/
...@@ -115,13 +117,22 @@ gitlab_shell: ...@@ -115,13 +117,22 @@ gitlab_shell:
# If you use non-standart ssh port you need to specify it # If you use non-standart ssh port you need to specify it
# ssh_port: 22 # ssh_port: 22
## Git settings ## Git settings
# CAUTION! # CAUTION!
# Use the default values unless you really know what you are doing # Use the default values unless you really know what you are doing
git: git:
bin_path: /usr/bin/git bin_path: /usr/bin/git
# Max size of git object like commit, in bytes # Max size of git object like commit, in bytes
# This value can be increased if you have a very large commits # This value can be increased if you have a very large commits
max_size: 5242880 # 5.megabytes max_size: 5242880 # 5.megabytes
# Git timeout to read commit, in seconds # Git timeout to read commit, in seconds
timeout: 10 timeout: 10
development:
<<: *base
test:
<<: *base
staging:
<<: *base
class Settings < Settingslogic class Settings < Settingslogic
source "#{Rails.root}/config/gitlab.yml" source "#{Rails.root}/config/gitlab.yml"
namespace Rails.env
class << self class << self
def gitlab_on_non_standard_port? def gitlab_on_non_standard_port?
...@@ -56,6 +57,7 @@ Settings.gitlab['support_email'] ||= Settings.gitlab.email_from ...@@ -56,6 +57,7 @@ Settings.gitlab['support_email'] ||= Settings.gitlab.email_from
Settings.gitlab['url'] ||= Settings.send(:build_gitlab_url) Settings.gitlab['url'] ||= Settings.send(:build_gitlab_url)
Settings.gitlab['user'] ||= 'git' Settings.gitlab['user'] ||= 'git'
Settings.gitlab['signup_enabled'] ||= false Settings.gitlab['signup_enabled'] ||= false
Settings.gitlab['username_changing_enabled'] = true if Settings.gitlab['username_changing_enabled'].nil?
# #
# Gravatar # Gravatar
......
module Gitlab module Gitlab
Version = File.read(Rails.root.join("VERSION")) VERSION = File.read(Rails.root.join("VERSION")).strip
Revision = `git log --pretty=format:'%h' -n 1` REVISION = `git log --pretty=format:'%h' -n 1`
def self.config def self.config
Settings Settings
......
...@@ -46,6 +46,11 @@ Gitlab::Application.routes.draw do ...@@ -46,6 +46,11 @@ Gitlab::Application.routes.draw do
root to: "projects#index" root to: "projects#index"
end end
#
# Attachments serving
#
get 'files/:type/:id/:filename' => 'files#download', constraints: { id: /\d+/, type: /[a-z]+/, filename: /[a-zA-Z.0-9_\-\+]+/ }
# #
# Admin Area # Admin Area
# #
......
root = Gitlab.config.gitolite.repos_path root = Gitlab.config.gitlab_shell.repos_path
projects = [ projects = [
{ path: 'underscore.git', git: 'https://github.com/documentcloud/underscore.git' }, { path: 'underscore.git', git: 'https://github.com/documentcloud/underscore.git' },
......
...@@ -16,7 +16,7 @@ Gitlab::Seeder.quiet do ...@@ -16,7 +16,7 @@ Gitlab::Seeder.quiet do
project_id: project.id, project_id: project.id,
author_id: user_id, author_id: user_id,
assignee_id: user_id, assignee_id: user_id,
closed: [true, false].sample, state: ['opened', 'closed'].sample,
milestone: project.milestones.sample, milestone: project.milestones.sample,
title: Faker::Lorem.sentence(6) title: Faker::Lorem.sentence(6)
}]) }])
......
...@@ -17,7 +17,7 @@ Gitlab::Seeder.quiet do ...@@ -17,7 +17,7 @@ Gitlab::Seeder.quiet do
project_id: project.id, project_id: project.id,
author_id: user_id, author_id: user_id,
assignee_id: user_id, assignee_id: user_id,
closed: [true, false].sample, state: ['opened', 'closed'].sample,
milestone: project.milestones.sample, milestone: project.milestones.sample,
title: Faker::Lorem.sentence(6) title: Faker::Lorem.sentence(6)
}]) }])
......
class RenameStateToMergeStatusInMilestone < ActiveRecord::Migration
def change
rename_column :merge_requests, :state, :merge_status
end
end
class AddStateToIssue < ActiveRecord::Migration
def change
add_column :issues, :state, :string
end
end
class AddStateToMergeRequest < ActiveRecord::Migration
def change
add_column :merge_requests, :state, :string
end
end
class AddStateToMilestone < ActiveRecord::Migration
def change
add_column :milestones, :state, :string
end
end
class ConvertClosedToStateInIssue < ActiveRecord::Migration
def up
Issue.transaction do
Issue.where(closed: true).update_all("state = 'closed'")
Issue.where(closed: false).update_all("state = 'opened'")
end
end
def down
Issue.transaction do
Issue.where(state: :closed).update_all("closed = 1")
end
end
end
class ConvertClosedToStateInMergeRequest < ActiveRecord::Migration
def up
MergeRequest.transaction do
MergeRequest.where("closed = 1 AND merged = 1").update_all("state = 'merged'")
MergeRequest.where("closed = 1 AND merged = 0").update_all("state = 'closed'")
MergeRequest.where("closed = 0").update_all("state = 'opened'")
end
end
def down
MergeRequest.transaction do
MergeRequest.where(state: :closed).update_all("closed = 1")
MergeRequest.where(state: :merged).update_all("closed = 1, merged = 1")
end
end
end
class ConvertClosedToStateInMilestone < ActiveRecord::Migration
def up
Milestone.transaction do
Milestone.where(closed: false).update_all("state = 'opened'")
Milestone.where(closed: false).update_all("state = 'active'")
end
end
def down
Milestone.transaction do
Milestone.where(state: :closed).update_all("closed = 1")
end
end
end
class RemoveMergedFromMergeRequest < ActiveRecord::Migration
def up
remove_column :merge_requests, :merged
end
def down
add_column :merge_requests, :merged, :boolean, default: true, null: false
end
end
class RemoveClosedFromIssue < ActiveRecord::Migration
def up
remove_column :issues, :closed
end
def down
add_column :issues, :closed, :boolean
end
end
class RemoveClosedFromMergeRequest < ActiveRecord::Migration
def up
remove_column :merge_requests, :closed
end
def down
add_column :merge_requests, :closed, :boolean
end
end
class RemoveClosedFromMilestone < ActiveRecord::Migration
def up
remove_column :milestones, :closed
end
def down
add_column :milestones, :closed, :boolean
end
end
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
# #
# It's strongly recommended to check this file into your version control system. # It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 20130131070232) do ActiveRecord::Schema.define(:version => 20130218141554) do
create_table "events", :force => true do |t| create_table "events", :force => true do |t|
t.string "target_type" t.string "target_type"
...@@ -39,16 +39,15 @@ ActiveRecord::Schema.define(:version => 20130131070232) do ...@@ -39,16 +39,15 @@ ActiveRecord::Schema.define(:version => 20130131070232) do
t.integer "project_id" t.integer "project_id"
t.datetime "created_at", :null => false t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false t.datetime "updated_at", :null => false
t.boolean "closed", :default => false, :null => false
t.integer "position", :default => 0 t.integer "position", :default => 0
t.string "branch_name" t.string "branch_name"
t.text "description" t.text "description"
t.integer "milestone_id" t.integer "milestone_id"
t.string "state"
end end
add_index "issues", ["assignee_id"], :name => "index_issues_on_assignee_id" add_index "issues", ["assignee_id"], :name => "index_issues_on_assignee_id"
add_index "issues", ["author_id"], :name => "index_issues_on_author_id" add_index "issues", ["author_id"], :name => "index_issues_on_author_id"
add_index "issues", ["closed"], :name => "index_issues_on_closed"
add_index "issues", ["created_at"], :name => "index_issues_on_created_at" add_index "issues", ["created_at"], :name => "index_issues_on_created_at"
add_index "issues", ["milestone_id"], :name => "index_issues_on_milestone_id" add_index "issues", ["milestone_id"], :name => "index_issues_on_milestone_id"
add_index "issues", ["project_id"], :name => "index_issues_on_project_id" add_index "issues", ["project_id"], :name => "index_issues_on_project_id"
...@@ -75,19 +74,17 @@ ActiveRecord::Schema.define(:version => 20130131070232) do ...@@ -75,19 +74,17 @@ ActiveRecord::Schema.define(:version => 20130131070232) do
t.integer "author_id" t.integer "author_id"
t.integer "assignee_id" t.integer "assignee_id"
t.string "title" t.string "title"
t.boolean "closed", :default => false, :null => false
t.datetime "created_at", :null => false t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false t.datetime "updated_at", :null => false
t.text "st_commits", :limit => 2147483647 t.text "st_commits", :limit => 2147483647
t.text "st_diffs", :limit => 2147483647 t.text "st_diffs", :limit => 2147483647
t.boolean "merged", :default => false, :null => false t.integer "merge_status", :default => 1, :null => false
t.integer "state", :default => 1, :null => false
t.integer "milestone_id" t.integer "milestone_id"
t.string "state"
end end
add_index "merge_requests", ["assignee_id"], :name => "index_merge_requests_on_assignee_id" add_index "merge_requests", ["assignee_id"], :name => "index_merge_requests_on_assignee_id"
add_index "merge_requests", ["author_id"], :name => "index_merge_requests_on_author_id" add_index "merge_requests", ["author_id"], :name => "index_merge_requests_on_author_id"
add_index "merge_requests", ["closed"], :name => "index_merge_requests_on_closed"
add_index "merge_requests", ["created_at"], :name => "index_merge_requests_on_created_at" add_index "merge_requests", ["created_at"], :name => "index_merge_requests_on_created_at"
add_index "merge_requests", ["milestone_id"], :name => "index_merge_requests_on_milestone_id" add_index "merge_requests", ["milestone_id"], :name => "index_merge_requests_on_milestone_id"
add_index "merge_requests", ["project_id"], :name => "index_merge_requests_on_project_id" add_index "merge_requests", ["project_id"], :name => "index_merge_requests_on_project_id"
...@@ -100,9 +97,9 @@ ActiveRecord::Schema.define(:version => 20130131070232) do ...@@ -100,9 +97,9 @@ ActiveRecord::Schema.define(:version => 20130131070232) do
t.integer "project_id", :null => false t.integer "project_id", :null => false
t.text "description" t.text "description"
t.date "due_date" t.date "due_date"
t.boolean "closed", :default => false, :null => false
t.datetime "created_at", :null => false t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false t.datetime "updated_at", :null => false
t.string "state"
end end
add_index "milestones", ["due_date"], :name => "index_milestones_on_due_date" add_index "milestones", ["due_date"], :name => "index_milestones_on_due_date"
......
...@@ -34,7 +34,6 @@ POST /projects/:id/milestones ...@@ -34,7 +34,6 @@ POST /projects/:id/milestones
Parameters: Parameters:
+ `id` (required) - The ID of a project + `id` (required) - The ID of a project
+ `milestone_id` (required) - The ID of a project milestone
+ `title` (required) - The title of an milestone + `title` (required) - The title of an milestone
+ `description` (optional) - The description of the milestone + `description` (optional) - The description of the milestone
+ `due_date` (optional) - The due date of the milestone + `due_date` (optional) - The due date of the milestone
......
...@@ -23,7 +23,7 @@ GET /projects ...@@ -23,7 +23,7 @@ GET /projects
"blocked": false, "blocked": false,
"created_at": "2012-05-23T08:00:58Z" "created_at": "2012-05-23T08:00:58Z"
}, },
"private": true, "public": true,
"path": "rails", "path": "rails",
"path_with_namespace": "rails/rails", "path_with_namespace": "rails/rails",
"issues_enabled": false, "issues_enabled": false,
...@@ -45,7 +45,7 @@ GET /projects ...@@ -45,7 +45,7 @@ GET /projects
"blocked": false, "blocked": false,
"created_at": "2012-05-23T08:00:58Z" "created_at": "2012-05-23T08:00:58Z"
}, },
"private": true, "public": true,
"path": "gitlab", "path": "gitlab",
"path_with_namespace": "randx/gitlab", "path_with_namespace": "randx/gitlab",
"issues_enabled": true, "issues_enabled": true,
...@@ -89,7 +89,7 @@ Parameters: ...@@ -89,7 +89,7 @@ Parameters:
"blocked": false, "blocked": false,
"created_at": "2012-05-23T08:00:58Z" "created_at": "2012-05-23T08:00:58Z"
}, },
"private": true, "public": true,
"path": "gitlab", "path": "gitlab",
"path_with_namespace": "randx/gitlab", "path_with_namespace": "randx/gitlab",
"issues_enabled": true, "issues_enabled": true,
......
...@@ -27,7 +27,7 @@ GitLab supports the following databases: ...@@ -27,7 +27,7 @@ GitLab supports the following databases:
mysql> \q mysql> \q
# Try connecting to the new database with the new user # Try connecting to the new database with the new user
sudo -u gitlab -H mysql -u gitlab -p -D gitlabhq_production sudo -u git -H mysql -u gitlab -p -D gitlabhq_production
## PostgreSQL ## PostgreSQL
...@@ -47,5 +47,5 @@ GitLab supports the following databases: ...@@ -47,5 +47,5 @@ GitLab supports the following databases:
template1=# \q template1=# \q
# Try connecting to the new database with the new user # Try connecting to the new database with the new user
sudo -u gitlab -H psql -d gitlabhq_production sudo -u git -H psql -d gitlabhq_production
This installation guide was created for Debian/Ubuntu and tested on it. This installation guide was created for Debian/Ubuntu and tested on it.
Please read `doc/install/requirements.md` for hardware and platform requirements. Please read [`doc/install/requirements.md`](./requirements.md) for hardware and platform requirements.
**Important Note:** **Important Note:**
...@@ -8,12 +8,13 @@ The following steps have been known to work. ...@@ -8,12 +8,13 @@ The following steps have been known to work.
If you deviate from this guide, do it with caution and make sure you don't If you deviate from this guide, do it with caution and make sure you don't
violate any assumptions GitLab makes about its environment. violate any assumptions GitLab makes about its environment.
For things like AWS installation scripts, init scripts or config files for For things like AWS installation scripts, init scripts or config files for
alternative web server have a look at the "Advanced Setup Tips" section. alternative web server have a look at the [`Advanced Setup
Tips`](./installation.md#advanced-setup-tips) section.
**Important Note:** **Important Note:**
If you find a bug/error in this guide please submit an issue or pull request If you find a bug/error in this guide please submit an issue or pull request
following the contribution guide (see `CONTRIBUTING.md`). following the [`contribution guide`](../../CONTRIBUTING.md).
- - - - - -
...@@ -24,7 +25,7 @@ The GitLab installation consists of setting up the following components: ...@@ -24,7 +25,7 @@ The GitLab installation consists of setting up the following components:
1. Packages / Dependencies 1. Packages / Dependencies
2. Ruby 2. Ruby
3. System Users 3. System Users
4. Gitolite 4. GitLab shell
5. Database 5. Database
6. GitLab 6. GitLab
7. Nginx 7. Nginx
...@@ -32,16 +33,13 @@ The GitLab installation consists of setting up the following components: ...@@ -32,16 +33,13 @@ The GitLab installation consists of setting up the following components:
# 1. Packages / Dependencies # 1. Packages / Dependencies
`sudo` is not installed on Debian by default. If you don't have it you'll need `sudo` is not installed on Debian by default. Make sure your system is
to install it first. up-to-date and install it.
# run as root # run as root
apt-get update && apt-get upgrade && apt-get install sudo apt-get update
apt-get upgrade
Make sure your system is up-to-date: apt-get install sudo
sudo apt-get update
sudo apt-get upgrade
**Note:** **Note:**
Vim is an editor that is used here whenever there are files that need to be Vim is an editor that is used here whenever there are files that need to be
...@@ -96,25 +94,24 @@ Create a `git` user for Gitlab: ...@@ -96,25 +94,24 @@ Create a `git` user for Gitlab:
# 4. GitLab shell # 4. GitLab shell
# login as git # Login as git
sudo su git sudo su git
# go to home directory # Go to home directory
cd /home/git cd /home/git
# clone gitlab shell # Clone gitlab shell
git clone https://github.com/gitlabhq/gitlab-shell.git git clone https://github.com/gitlabhq/gitlab-shell.git
# setup # Setup
cd gitlab-shell cd gitlab-shell
cp config.yml.example config.yml cp config.yml.example config.yml
./bin/install ./bin/install
# 5. Database # 5. Database
To setup the MySQL/PostgreSQL database and dependencies please see [`doc/install/databases.md`](./databases.md) . To setup the MySQL/PostgreSQL database and dependencies please see [`doc/install/databases.md`](./databases.md).
# 6. GitLab # 6. GitLab
...@@ -154,9 +151,13 @@ do so with caution! ...@@ -154,9 +151,13 @@ do so with caution!
sudo chmod -R u+rwX log/ sudo chmod -R u+rwX log/
sudo chmod -R u+rwX tmp/ sudo chmod -R u+rwX tmp/
# Make directory for satellites # Create directory for satellites
sudo -u git -H mkdir /home/git/gitlab-satellites sudo -u git -H mkdir /home/git/gitlab-satellites
# Create directory for pids and make sure GitLab can write to it
sudo -u git -H mkdir tmp/pids/
sudo chmod -R u+rwX tmp/pids/
# Copy the example Unicorn config # Copy the example Unicorn config
sudo -u git -H cp config/unicorn.rb.example config/unicorn.rb sudo -u git -H cp config/unicorn.rb.example config/unicorn.rb
...@@ -188,6 +189,8 @@ Make sure to update username/password in config/database.yml. ...@@ -188,6 +189,8 @@ Make sure to update username/password in config/database.yml.
## Initialise Database and Activate Advanced Features ## Initialise Database and Activate Advanced Features
sudo -u git -H bundle exec rake db:setup RAILS_ENV=production
sudo -u git -H bundle exec rake db:seed_fu RAILS_ENV=production
sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production
...@@ -205,7 +208,7 @@ Make GitLab start on boot: ...@@ -205,7 +208,7 @@ Make GitLab start on boot:
## Check Application Status ## Check Application Status
Check if GitLab and its environment is configured correctly: Check if GitLab and its environment are configured correctly:
sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
...@@ -227,7 +230,7 @@ However there are still a few steps left. ...@@ -227,7 +230,7 @@ However there are still a few steps left.
**Note:** **Note:**
If you can't or don't want to use Nginx as your web server, have a look at the If you can't or don't want to use Nginx as your web server, have a look at the
"Advanced Setup Tips" section. [`Advanced Setup Tips`](./installation.md#advanced-setup-tips) section.
## Installation ## Installation
sudo apt-get install nginx sudo apt-get install nginx
...@@ -244,11 +247,11 @@ Make sure to edit the config file to match your setup: ...@@ -244,11 +247,11 @@ Make sure to edit the config file to match your setup:
# Change **YOUR_SERVER_IP** and **YOUR_SERVER_FQDN** # Change **YOUR_SERVER_IP** and **YOUR_SERVER_FQDN**
# to the IP address and fully-qualified domain name # to the IP address and fully-qualified domain name
# of your host serving GitLab # of your host serving GitLab
sudo vim /etc/nginx/sites-enabled/gitlab sudo vim /etc/nginx/sites-available/gitlab
## Restart ## Restart
sudo /etc/init.d/nginx restart sudo service nginx restart
# Done! # Done!
...@@ -282,7 +285,7 @@ a different host, you can configure its connection string via the ...@@ -282,7 +285,7 @@ a different host, you can configure its connection string via the
## Custom SSH Connection ## Custom SSH Connection
If you are running SSH on a non-standard port, you must change the gitlab user'S SSH config. If you are running SSH on a non-standard port, you must change the gitlab user's SSH config.
# Add to /home/git/.ssh/config # Add to /home/git/.ssh/config
host localhost # Give your setup a name (here: override localhost) host localhost # Give your setup a name (here: override localhost)
......
...@@ -43,6 +43,6 @@ class ProfileSshKeys < Spinach::FeatureSteps ...@@ -43,6 +43,6 @@ class ProfileSshKeys < Spinach::FeatureSteps
end end
And 'I have ssh key "ssh-rsa Work"' do And 'I have ssh key "ssh-rsa Work"' do
create(:key, :user => @user, :title => "ssh-rsa Work", :key => "jfKLJDFKSFJSHFJssh-rsa Work") create(:key, :user => @user, :title => "ssh-rsa Work", :key => "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC+L3TbFegm3k8QjejSwemk4HhlRh+DuN679Pc5ckqE/MPhVtE/+kZQDYCTB284GiT2aIoGzmZ8ee9TkaoejAsBwlA+Wz2Q3vhz65X6sMgalRwpdJx8kSEUYV8ZPV3MZvPo8KdNg993o4jL6G36GDW4BPIyO6FPZhfsawdf6liVD0Xo5kibIK7B9VoE178cdLQtLpS2YolRwf5yy6XR6hbbBGQR+6xrGOdP16eGZDb1CE2bMvvJijjloFqPscGktWOqW+nfh5txwFfBzlfARDTBsS8WZtg3Yoj1kn33kPsWRlgHfNutFRAIynDuDdQzQq8tTtVwm+Yi75RfcPHW8y3P Work")
end end
end end
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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