Merge branch 'master' into fixes/api

Conflicts:
	spec/requests/api/projects_spec.rb
This commit is contained in:
Sebastian Ziebell 2013-02-20 12:43:32 +01:00
commit eefb27f5ae
134 changed files with 1116 additions and 859 deletions

View file

@ -49,6 +49,10 @@ $ ->
# Bottom tooltip
$('.has_bottom_tooltip').tooltip(placement: 'bottom')
# Form submitter
$('.trigger-submit').on 'change', ->
$(@).parents('form').submit()
# Flash
if (flash = $("#flash-container")).length > 0
flash.click -> $(@).slideUp("slow")

View file

@ -27,7 +27,7 @@ class MergeRequest
this.$el.find(selector)
initMergeWidget: ->
this.showState( @opts.current_state )
this.showState( @opts.current_status )
if this.$('.automerge_widget').length and @opts.check_enable
$.get @opts.url_to_automerge_check, (data) =>

View file

@ -63,7 +63,7 @@
color: $style_color;
text-shadow: 0 1px 1px #FFF;
font-family: 'Yanone', sans-serif;
font-size: 26px;
line-height: 42px;
font-size: 24px;
line-height: 36px;
font-weight: normal;
}

View file

@ -29,7 +29,7 @@
a{
color: $style_color;
}
> span {
font-family: $monospace_font;
font-size: 14px;
@ -124,7 +124,7 @@
.wrap{
display: inline-block;
}
.frame {
display: inline-block;
background-color: #fff;
@ -149,7 +149,7 @@
.view.swipe{
position: relative;
.swipe-frame{
display: block;
margin: auto;
@ -228,7 +228,7 @@
bottom: 0px;
left: 50%;
margin-left: -150px;
.drag-track{
display: block;
position: absolute;
@ -237,7 +237,7 @@
width: 276px;
background: url('onion_skin_sprites.gif') -4px -20px repeat-x;
}
.dragger {
display: block;
position: absolute;
@ -248,7 +248,7 @@
background: url('onion_skin_sprites.gif') 0px -34px repeat-x;
cursor: pointer;
}
.transparent {
display: block;
position: absolute;
@ -258,7 +258,7 @@
width: 10px;
background: url('onion_skin_sprites.gif') -2px 0px no-repeat;
}
.opaque {
display: block;
position: absolute;
@ -275,19 +275,19 @@
padding: 10px;
text-align: center;
background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf));
background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf);
background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf);
ul, li{
list-style: none;
margin: 0;
padding: 0;
display: inline-block;
}
li{
color: grey;
border-left: 1px solid #c1c1c1;
@ -322,12 +322,12 @@
}
.commit-author, .commit-committer{
display: block;
color: #999;
font-weight: normal;
color: #999;
font-weight: normal;
font-style: italic;
}
.commit-author strong, .commit-committer strong{
font-weight: bold;
font-weight: bold;
font-style: normal;
}
@ -337,7 +337,6 @@
*/
.commit {
.browse_code_link_holder {
@extend .span2;
float: right;
}

View file

@ -5,15 +5,16 @@
header {
&.navbar-gitlab {
.navbar-inner {
height: 45px;
padding: 5px;
height: 40px;
padding: 3px;
background: #F1F1F1;
filter: none;
.nav > li > a {
color: $style_color;
text-shadow: 0 1px 0 #fff;
font-size: 18px;
padding: 12px;
font-size: 16px;
padding: 10px;
}
/** NAV block with links and profile **/
@ -25,7 +26,6 @@ header {
}
z-index: 10;
/*height: 60px;*/
/**
*
@ -34,7 +34,7 @@ header {
*/
.app_logo {
float: left;
margin-right: 15px;
margin-right: 9px;
position: relative;
top: -5px;
padding-top: 5px;
@ -42,10 +42,10 @@ header {
a {
float: left;
padding: 0px;
margin: 0 10px;
margin: 0 6px;
h1 {
background: url('logo_dark.png') no-repeat 0px 2px;
background: url('logo_dark.png') no-repeat center 1px;
float: left;
height: 40px;
width: 40px;
@ -79,7 +79,6 @@ header {
.search {
margin-right: 45px;
margin-left: 10px;
margin-top: 2px;
.search-input {
@extend .span2;
@ -105,7 +104,7 @@ header {
.account-box {
position: absolute;
right: 0;
top: 6px;
top: 4px;
z-index: 10000;
width: 128px;
font-size: 11px;
@ -228,6 +227,7 @@ header {
.search-input {
background-color: #D2D5DA;
background-color: rgba(255, 255, 255, 0.5);
border: 1px solid #AAA;
&:focus {
background-color: white;
@ -240,13 +240,16 @@ header {
.app_logo {
a {
h1 {
background: url('logo_white.png') no-repeat center center;
background: url('logo_white.png') no-repeat center 1px;
color: #fff;
text-shadow: 0 1px 1px #111;
}
}
}
.project_name {
a {
color: #FFF;
}
color: #fff;
text-shadow: 0 1px 1px #111;
}
@ -261,11 +264,11 @@ header {
.separator {
float: left;
height: 60px;
height: 46px;
width: 1px;
background: white;
border-left: 1px solid #DDD;
margin-top: -10px;
margin-top: -3px;
margin-left: 10px;
margin-right: 10px;
}

View file

@ -115,3 +115,7 @@ ul.nav.nav-projects-tabs {
}
}
}
.team_member_row form {
margin: 0px;
}

View file

@ -8,66 +8,27 @@
*
*/
.ui_mars {
/*
* Application Header
*
*/
header {
@extend .header-dark;
&.navbar-gitlab {
.navbar-inner {
background: #474D57 url('bg-header.png') repeat-x bottom;
border-bottom: 1px solid #444;
.nav > li > a {
color: #eee;
text-shadow: 0 1px 0 #444;
background: #474D57;
border-bottom: 1px solid #373D47;
.app_logo {
&:hover {
background-color: #373D47;
}
}
}
}
.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 {
a {
h1 {
background: url('logo_white.png') no-repeat center center;
color: #eee;
text-shadow: 0 1px 1px #111;
}
}
&:hover {
background-color: #41464e;
}
}
.project_name {
color: #eee;
text-shadow: 0 1px 1px #111;
.separator {
background: #31363E;
border-left: 1px solid #666;
}
}
.separator {
background: #31363E;
border-left: 1px solid #666;
}
/*
* End of Application Header
*
*/
}

View file

@ -14,7 +14,7 @@ class MergeRequestsLoadContext < BaseContext
end
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)?
if params[:assignee_id].present?

View file

@ -38,6 +38,8 @@ module Projects
if @project.valid? && @project.import_url.present?
shell = Gitlab::Shell.new
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
else
@project.errors.add(:import_url, 'cannot clone repo')

View file

@ -5,7 +5,7 @@ class DashboardController < ApplicationController
before_filter :event_filter, only: :show
def show
@groups = current_user.authorized_groups
@groups = current_user.authorized_groups.sort_by(&:human_name)
@has_authorized_projects = @projects.count > 0
@teams = current_user.authorized_teams
@projects_count = @projects.count

View file

@ -0,0 +1,13 @@
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

View file

@ -73,14 +73,14 @@ class MergeRequestsController < ProjectResourceController
if @merge_request.unchecked?
@merge_request.check_if_can_be_merged
end
render json: {state: @merge_request.human_state}
render json: {merge_status: @merge_request.human_merge_status}
rescue Gitlab::SatelliteNotExistError
render json: {state: :no_satellite}
render json: {merge_status: :no_satellite}
end
def automerge
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.automerge!(current_user)
@status = true

View file

@ -12,7 +12,7 @@ class MilestonesController < ProjectResourceController
def index
@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")
else @project.milestones.active.order("due_date ASC")
end

View file

@ -51,7 +51,9 @@ class ProfilesController < ApplicationController
end
def update_username
@user.update_attributes(username: params[:user][:username])
if @user.can_change_username?
@user.update_attributes(username: params[:user][:username])
end
respond_to do |format|
format.js

View file

@ -4,7 +4,11 @@ class TeamMembersController < ProjectResourceController
before_filter :authorize_admin_project!, except: [:index, :show]
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
def show

View file

@ -27,7 +27,13 @@ class Teams::MembersController < Teams::ApplicationController
end
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)
redirect_to team_members_path(user_team), notice: "Membership for #{team_member.name} was successfully updated in Team of users."
else
@ -45,5 +51,4 @@ class Teams::MembersController < Teams::ApplicationController
def team_member
@member ||= user_team.members.find_by_username(params[:id])
end
end

View file

@ -9,13 +9,11 @@ class TeamsController < ApplicationController
layout 'user_team', except: [:new, :create]
def show
user_team
projects
@events = Event.in_projects(user_team.project_ids).limit(20).offset(params[:offset] || 0)
end
def edit
user_team
end
def update
@ -41,6 +39,9 @@ class TeamsController < ApplicationController
@team.path = @team.name.dup.parameterize if @team.name
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)
else
render action: :new

View file

@ -73,8 +73,8 @@ module ApplicationHelper
def search_autocomplete_source
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) } }
teams = current_user.authorized_teams.map { |team| { label: "team: #{team.name}", url: team_path(team) } }
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: #{simple_sanitize(team.name)}", url: team_path(team) } }
default_nav = [
{ label: "My Profile", url: profile_path },
@ -159,8 +159,13 @@ module ApplicationHelper
alt: "Sign in with #{provider.to_s.titleize}")
end
def simple_sanitize str
sanitize(str, tags: %w(a span))
end
def image_url(source)
root_url + path_to_image(source)
end
alias_method :url_to_image, :image_url
end

View file

@ -27,6 +27,6 @@ module DashboardHelper
items.opened
end
items.where(assignee_id: current_user.id).count
items.cared(current_user).count
end
end

View file

@ -6,7 +6,7 @@ module IssuesHelper
def issue_css_classes issue
classes = "issue"
classes << " closed" if issue.closed
classes << " closed" if issue.closed?
classes << " today" if issue.today?
classes
end

View file

@ -12,7 +12,7 @@ module MergeRequestsHelper
def mr_css_classes mr
classes = "merge_request"
classes << " closed" if mr.closed
classes << " closed" if mr.closed?
classes << " merged" if mr.merged?
classes
end

View file

@ -10,8 +10,8 @@ module NamespacesHelper
global_opts = ["Global", [['/', Namespace.global_id]] ]
group_opts = ["Groups", groups.map {|g| [g.human_name, g.id]} ]
users_opts = [ "Users", users.map {|u| [u.human_name, u.id]} ]
group_opts = ["Groups", groups.sort_by(&:human_name).map {|g| [g.human_name, g.id]} ]
users_opts = [ "Users", users.sort_by(&:human_name).map {|u| [u.human_name, u.id]} ]
options = []
options << global_opts if current_user.admin

View file

@ -1,12 +1,4 @@
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)
"You are going to remove #{user.name} from #{project.name} project team. Are you sure?"
end
@ -56,7 +48,7 @@ module ProjectsHelper
def project_title project
if project.group
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
else
project.name

View file

@ -123,7 +123,7 @@ class Ability
def user_team_abilities user, team
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?
rules << [ :manage_user_team ]
end

View file

@ -17,10 +17,9 @@ module Issuable
validates :project, presence: true
validates :author, presence: true
validates :title, presence: true, length: { within: 0..255 }
validates :closed, inclusion: { in: [true, false] }
scope :opened, -> { where(closed: false) }
scope :closed, -> { where(closed: true) }
scope :opened, -> { with_state(:opened) }
scope :closed, -> { with_state(:closed) }
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 :assigned, ->(u) { where(assignee_id: u.id)}
@ -62,14 +61,6 @@ module Issuable
assignee_id_changed?
end
def is_being_closed?
closed_changed? && closed
end
def is_being_reopened?
closed_changed? && !closed
end
#
# Votes
#

View file

@ -130,10 +130,6 @@ class Event < ActiveRecord::Base
target if target_type == "MergeRequest"
end
def author
@author ||= User.find(author_id)
end
def action_name
if closed?
"closed"

View file

@ -9,7 +9,7 @@
# project_id :integer
# created_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)
# branch_name :string(255)
# description :text
@ -19,12 +19,35 @@
class Issue < ActiveRecord::Base
include Issuable
attr_accessible :title, :assignee_id, :closed, :position, :description,
:milestone_id, :label_list, :author_id_of_changes
attr_accessible :title, :assignee_id, :position, :description,
:milestone_id, :label_list, :author_id_of_changes,
:state_event
acts_as_taggable_on :labels
def self.open_for(user)
opened.assigned(user)
class << self
def cared(user)
where('assignee_id = :user', user: user.id)
end
def open_for(user)
opened.assigned(user)
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

View file

@ -35,7 +35,7 @@ class Key < ActiveRecord::Base
def fingerprintable_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')
begin
file.puts key
@ -45,7 +45,7 @@ class Key < ActiveRecord::Base
file.close
file.unlink # deletes the temp file
end
errors.add(:key, "can't be fingerprinted") if fingerprint_output.match("failed")
errors.add(:key, "can't be fingerprinted") if $?.exitstatus != 0
end
def set_identifier

View file

@ -9,15 +9,14 @@
# author_id :integer
# assignee_id :integer
# title :string(255)
# closed :boolean default(FALSE), not null
# state :string(255) not null
# created_at :datetime not null
# updated_at :datetime not null
# st_commits :text(2147483647)
# st_diffs :text(2147483647)
# merged :boolean default(FALSE), not null
# state :integer default(1), not null
# milestone_id :integer
# merge_status :integer default(1), not null
#
# milestone_id :integer
require Rails.root.join("app/models/commit")
require Rails.root.join("lib/static_model")
@ -25,11 +24,33 @@ require Rails.root.join("lib/static_model")
class MergeRequest < ActiveRecord::Base
include Issuable
attr_accessible :title, :assignee_id, :closed, :target_branch, :source_branch, :milestone_id,
:author_id_of_changes
attr_accessible :title, :assignee_id, :target_branch, :source_branch, :milestone_id,
:author_id_of_changes, :state_event
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"
UNCHECKED = 1
@ -43,21 +64,33 @@ class MergeRequest < ActiveRecord::Base
validates :target_branch, presence: true
validate :validate_branches
def self.find_all_by_branch(branch_name)
where("source_branch LIKE :branch OR target_branch LIKE :branch", 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)
end
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)
end
end
def self.find_all_by_milestone(milestone)
where("milestone_id = :milestone_id", milestone_id: milestone)
end
def human_state
states = {
def human_merge_status
merge_statuses = {
CAN_BE_MERGED => "can_be_merged",
CANNOT_BE_MERGED => "cannot_be_merged",
UNCHECKED => "unchecked"
}
states[self.state]
merge_statuses[self.merge_status]
end
def validate_branches
@ -72,20 +105,20 @@ class MergeRequest < ActiveRecord::Base
end
def unchecked?
state == UNCHECKED
merge_status == UNCHECKED
end
def mark_as_unchecked
self.state = UNCHECKED
self.merge_status = UNCHECKED
self.save
end
def can_be_merged?
state == CAN_BE_MERGED
merge_status == CAN_BE_MERGED
end
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
else
CANNOT_BE_MERGED
@ -98,7 +131,7 @@ class MergeRequest < ActiveRecord::Base
end
def reloaded_diffs
if open? && unmerged_diffs.any?
if opened? && unmerged_diffs.any?
self.st_diffs = unmerged_diffs
self.save
end
@ -128,10 +161,6 @@ class MergeRequest < ActiveRecord::Base
commits.first
end
def merged?
merged && merge_event
end
def merge_event
self.project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::MERGED).last
end
@ -146,26 +175,16 @@ class MergeRequest < ActiveRecord::Base
def probably_merged?
unmerged_commits.empty? &&
commits.any? && open?
end
def open?
!closed
end
def mark_as_merged!
self.merged = true
self.closed = true
save
commits.any? && opened?
end
def mark_as_unmergable
self.state = CANNOT_BE_MERGED
self.merge_status = CANNOT_BE_MERGED
self.save
end
def reloaded_commits
if open? && unmerged_commits.any?
if opened? && unmerged_commits.any?
self.st_commits = unmerged_commits
save
end
@ -181,7 +200,8 @@ class MergeRequest < ActiveRecord::Base
end
def merge!(user_id)
self.mark_as_merged!
self.merge
Event.create(
project: self.project,
action: Event::MERGED,

View file

@ -13,19 +13,32 @@
#
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
belongs_to :project
has_many :issues
has_many :merge_requests
scope :active, -> { where(closed: false) }
scope :closed, -> { where(closed: true) }
scope :active, -> { with_state(:active) }
scope :closed, -> { with_state(:closed) }
validates :title, 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?
if due_date
@ -68,17 +81,13 @@ class Milestone < ActiveRecord::Base
end
def can_be_closed?
open? && issues.opened.count.zero?
active? && issues.opened.count.zero?
end
def is_empty?
total_items_count.zero?
end
def open?
!closed
end
def author_id
author_id_of_changes
end

View file

@ -17,11 +17,15 @@ class Namespace < ActiveRecord::Base
has_many :projects, dependent: :destroy
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 },
format: { with: Gitlab::Regex.path_regex,
message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
validates :owner, presence: true
delegate :name, to: :owner, allow_nil: true, prefix: true

View file

@ -43,7 +43,7 @@ class Project < ActiveRecord::Base
has_many :events, 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 :users_projects, dependent: :destroy
has_many :notes, dependent: :destroy
@ -146,7 +146,7 @@ class Project < ActiveRecord::Base
end
def saved?
id && valid?
id && persisted?
end
def import?

View file

@ -132,16 +132,16 @@ class Repository
return nil unless commit
# 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")
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
prefix = self.path_with_namespace + "/"
# Create file if not exists
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)
end

View file

@ -234,8 +234,12 @@ class User < ActiveRecord::Base
keys.count == 0
end
def can_change_username?
Gitlab.config.gitlab.username_changing_enabled
end
def can_create_project?
projects_limit > personal_projects.count
projects_limit > owned_projects.count
end
def can_create_group?
@ -263,7 +267,7 @@ class User < ActiveRecord::Base
end
def cared_merge_requests
MergeRequest.where("author_id = :id or assignee_id = :id", id: self.id)
MergeRequest.cared(self)
end
# Remove user from all projects and

View file

@ -21,8 +21,11 @@ class UserTeam < ActiveRecord::Base
has_many :projects, through: :user_team_project_relationships
has_many :members, through: :user_team_user_relationships, source: :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 },
format: { with: Gitlab::Regex.path_regex,
message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }

View file

@ -26,6 +26,10 @@ class UserTeamProjectRelationship < ActiveRecord::Base
user_team.name
end
def human_max_access
UserTeam.access_roles.key(greatest_access)
end
private
def check_greatest_access

View file

@ -20,15 +20,23 @@ class ActivityObserver < ActiveRecord::Observer
end
end
def after_save(record)
if record.changed.include?("closed") && record.author_id_of_changes
def after_close(record, transition)
Event.create(
project: record.project,
target_id: record.id,
target_type: record.class.name,
action: (record.closed ? Event::CLOSED : Event::REOPENED),
action: Event::CLOSED,
author_id: record.author_id_of_changes
)
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

View file

@ -7,22 +7,31 @@ class IssueObserver < ActiveRecord::Observer
end
end
def after_update(issue)
def after_close(issue, transition)
send_reassigned_email(issue) if issue.is_being_reassigned?
status = nil
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
create_note(issue)
end
def after_reopen(issue, transition)
send_reassigned_email(issue) if issue.is_being_reassigned?
create_note(issue)
end
def after_update(issue)
send_reassigned_email(issue) if issue.is_being_reassigned?
end
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)
recipient_ids = [issue.assignee_id, issue.assignee_id_was].keep_if {|id| id && id != current_user.id }

View file

@ -7,15 +7,20 @@ class MergeRequestObserver < ActiveRecord::Observer
end
end
def after_update(merge_request)
def after_close(merge_request, transition)
send_reassigned_email(merge_request) if merge_request.is_being_reassigned?
status = nil
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
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?
Note.create_status_change_note(merge_request, current_user, merge_request.state)
end
def after_update(merge_request)
send_reassigned_email(merge_request) if merge_request.is_being_reassigned?
end
protected

View file

@ -19,4 +19,12 @@ class AttachmentUploader < CarrierWave::Uploader::Base
rescue
false
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

View file

@ -28,7 +28,7 @@
%td= group.path
%td= group.projects.count
%td
= link_to group.owner_name, admin_user_path(group.owner_id)
= link_to group.owner_name, admin_user_path(group.owner)
%td.bgred
= 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"

View file

@ -30,7 +30,7 @@
%td= team.projects.count
%td= team.members.count
%td
= link_to team.owner.name, admin_user_path(team.owner_id)
= link_to team.owner.name, admin_user_path(team.owner)
%td.bgred
= 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"

View file

@ -6,9 +6,9 @@
= link_to commit.short_id(8), project_commit_path(@project, commit), class: "commit_short_id"
= commit.author_link avatar: true, size: 24
&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)
ago
&nbsp;

View file

@ -26,7 +26,7 @@
= markdown truncate(event.target.note, length: 70)
- note = event.target
- 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?
= image_tag note.attachment.url, class: 'note-image-attach'
- else

View file

@ -26,6 +26,8 @@
= link_to group_filter_path(entity, project_id: project.id) do
= project.name_with_namespace
%small.pull-right= entities_per_project(project, entity)
- if @projects.blank?
%p.nothing_here_message This group has no projects yet
%fieldset
%hr

View file

@ -7,6 +7,8 @@
= link_to people_group_path(@group, project_id: project.id) do
= project.name_with_namespace
%small.pull-right= project.users.count
- if @projects.blank?
%p.nothing_here_message This group has no projects yet
%fieldset
%hr

View file

@ -30,6 +30,8 @@
= 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 '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
.ui-box

View file

@ -1,8 +1,8 @@
%h3.page_title
GITLAB
.pull-right
%span= Gitlab::Version
%small= Gitlab::Revision
%span= Gitlab::VERSION
%small= Gitlab::REVISION
%hr
%p.lead
Self Hosted Git Management

View file

@ -8,10 +8,10 @@
%i.icon-comment
= issue.notes.count
- if can? current_user, :modify_issue, issue
- 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
- if issue.closed?
= 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
= 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
%i.icon-edit
Edit

View file

@ -7,10 +7,10 @@
%span.pull-right
- if can?(current_user, :admin_project, @project) || @issue.author == current_user
- if @issue.closed
= link_to 'Reopen', project_issue_path(@project, @issue, issue: {closed: false }, status_only: true), method: :put, class: "btn grouped reopen_issue"
- if @issue.closed?
= link_to 'Reopen', project_issue_path(@project, @issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn grouped reopen_issue"
- 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
= link_to edit_project_issue_path(@project, @issue), class: "btn grouped" do
%i.icon-edit
@ -27,7 +27,7 @@
.ui-box.ui-box-show
.ui-box-head
%h4.box-title
- if @issue.closed
- if @issue.closed?
.error.status_info Closed
= gfm escape_once(@issue.title)

View file

@ -29,10 +29,10 @@
$(function(){
merge_request = new MergeRequest({
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)}",
ci_enable: #{@project.gitlab_ci? ? "true" : "false"},
current_state: "#{@merge_request.human_state}",
current_status: "#{@merge_request.human_merge_status}",
action: "#{controller.action_name}"
});
});

View file

@ -3,7 +3,7 @@
%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"}
.alert.alert-success
%span

View file

@ -1,11 +1,11 @@
.ui-box.ui-box-show
.ui-box-head
%h4.box-title
- if @merge_request.merged
- if @merge_request.merged?
.error.status_info
%i.icon-ok
Merged
- elsif @merge_request.closed
- elsif @merge_request.closed?
.error.status_info Closed
= gfm escape_once(@merge_request.title)
@ -21,14 +21,14 @@
%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
- if @merge_request.merged?
%span
Merged by #{link_to_member(@project, @merge_request.merge_event.author)}
%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.
%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?
.ui-box-bottom
%span
Merged by #{link_to_member(@project, @merge_request.merge_event.author)}
%small #{time_ago_in_words(@merge_request.merge_event.created_at)} ago.

View file

@ -1,4 +1,4 @@
- if @merge_request.open? && @commits.any?
- if @merge_request.opened? && @commits.any?
.ci_widget.ci-success{style: "display:none"}
.alert.alert-success
%i.icon-ok

View file

@ -7,7 +7,7 @@
%span.pull-right
- if can?(current_user, :modify_merge_request, @merge_request)
- if @merge_request.open?
- if @merge_request.opened?
.left.btn-group
%a.btn.grouped.dropdown-toggle{ data: {toggle: :dropdown} }
%i.icon-download-alt
@ -17,7 +17,7 @@
%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)
= 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
%i.icon-edit

View file

@ -1,12 +1,12 @@
%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
- 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
%i.icon-edit
Edit
%h4
= 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)
%small
= milestone.expires_at

View file

@ -9,7 +9,7 @@
&larr; To milestones list
.span6
.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
%i.icon-plus
New Issue
@ -25,12 +25,12 @@
%hr
%p
%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-head
%h4.box-title
- if @milestone.closed
- if @milestone.closed?
.error.status_info Closed
- elsif @milestone.expired?
.error.status_info Expired
@ -63,7 +63,7 @@
%li=link_to('All Issues', '#')
%ul.well-list
- @issues.each do |issue|
%li{data: {closed: issue.closed}}
%li{data: {closed: issue.closed?}}
= link_to [@project, issue] do
%span.badge.badge-info ##{issue.id}
&ndash;
@ -77,7 +77,7 @@
%li=link_to('All Merge Requests', '#')
%ul.well-list
- @merge_requests.each do |merge_request|
%li{data: {closed: merge_request.closed}}
%li{data: {closed: merge_request.closed?}}
= link_to [@project, merge_request] do
%span.badge.badge-info ##{merge_request.id}
&ndash;

View file

@ -31,7 +31,7 @@
- if note.attachment.image?
= image_tag note.attachment.url, class: 'note-image-attach'
.attachment.pull-right
= link_to note.attachment.url, target: "_blank" do
= link_to note.attachment.secure_url, target: "_blank" do
%i.icon-paper-clip
= note.attachment_identifier
.clear

View file

@ -53,29 +53,30 @@
%fieldset.update-username
%legend
Username
%small.cred.pull-right
Changing your username can have unintended side effects!
= form_for @user, url: update_username_profile_path, method: :put, remote: true do |f|
.padded
= f.label :username
.input
= f.text_field :username, required: true
&nbsp;
%span.loading-gif.hide= image_tag "ajax_loader.gif"
%span.update-success.cgreen.hide
%i.icon-ok
Saved
%span.update-failed.cred.hide
%i.icon-remove
Failed
%ul.cred
%li It will change web url for personal projects.
%li It will change the git path to repositories for personal projects.
.input
= f.submit 'Save username', class: "btn btn-save"
- if current_user.can_change_username?
%fieldset.update-username
%legend
Username
%small.cred.pull-right
Changing your username can have unintended side effects!
= form_for @user, url: update_username_profile_path, method: :put, remote: true do |f|
.padded
= f.label :username
.input
= f.text_field :username, required: true
&nbsp;
%span.loading-gif.hide= image_tag "ajax_loader.gif"
%span.update-success.cgreen.hide
%i.icon-ok
Saved
%span.update-failed.cred.hide
%i.icon-remove
Failed
%ul.cred
%li It will change web url for personal projects.
%li It will change the git path to repositories for personal projects.
.input
= f.submit 'Save username', class: "btn btn-save"
- if Gitlab.config.gitlab.signup_enabled
%fieldset.remove-account
@ -83,4 +84,4 @@
Remove account
%small.cred.pull-right
Before removing the account you must remove all projects!
= link_to 'Delete account', user_registration_path, confirm: "REMOVE #{current_user.name}? Are you sure?", method: :delete, class: "btn btn-remove delete-key btn-small pull-right"
= link_to 'Delete account', user_registration_path, confirm: "REMOVE #{current_user.name}? Are you sure?", method: :delete, class: "btn btn-remove delete-key btn-small pull-right"

View file

@ -77,7 +77,7 @@
%legend
Personal projects:
%small.pull-right
%span= current_user.personal_projects.count
%span= current_user.owned_projects.count
of
%span= current_user.projects_limit
.padded

View file

@ -28,7 +28,7 @@
.input
= f.text_field :import_url, class: 'xlarge', placeholder: 'https://github.com/randx/six.git'
.light
URL should be clonable
URL must be clonable
%p.padded
New projects are private by default. You choose who can see the project and commit to repository.

View file

@ -9,7 +9,7 @@
- @notes.each do |note|
%tr
%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"
= note.attachment_identifier
%td

View file

@ -0,0 +1,10 @@
%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}

View file

@ -0,0 +1,4 @@
.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

View file

@ -1,15 +0,0 @@
- 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

View file

@ -1,16 +1,8 @@
- grouper_project_members(@project).each do |access, members|
- team.each do |access, members|
.ui-box
%h5.title
= Project.access_options.key(access).pluralize
%small= members.size
%ul.well-list
- members.sort_by(&:user_name).each do |up|
= render(partial: 'team_members/show', locals: {member: up})
:javascript
$(function(){
$('.repo-access-select, .project-access-select').live("change", function() {
$(this.form).submit();
});
})
- members.sort_by(&:user_name).each do |team_member|
= render 'team_members/team_member', member: team_member

View file

@ -2,7 +2,7 @@
- allow_admin = can? current_user, :admin_project, @project
%li{id: dom_id(user), class: "team_member_row user_#{user.id}"}
.row
.span6
.span4
= link_to project_team_member_path(@project, user), title: user.name, class: "dark" do
= image_tag gravatar_icon(user.email, 40), class: "avatar s32"
= link_to project_team_member_path(@project, user), title: user.name, class: "dark" do
@ -10,18 +10,18 @@
%br
%small.cgray= user.email
.span5.pull-right
.span4.pull-right
- if allow_admin
.left
= 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
- if current_user == user
%span.btn.disabled This is you!
%span.label This is you!
- if @project.namespace_owner == user
%span.btn.disabled Owner
%span.label Owner
- elsif user.blocked
%span.btn.disabled.blocked Blocked
%span.label Blocked
- 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
%i.icon-minus.icon-white

View file

@ -1,16 +0,0 @@
- 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();
});
})

View file

@ -18,16 +18,39 @@
%hr
.clearfix
%div.team-table
= render partial: "team_members/team", locals: {project: @project}
.row
.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?
%h5
Assigned teams
(#{@project.user_teams.count})
%div
= render "team_members/assigned_teams", assigned_teams: @assigned_teams
.span9
%div.team-table
= render "team_members/team", team: @team
%h3.page_title
Assigned teams
(#{@project.user_teams.count})
%hr
.clearfix
%div.team-table
= render partial: "team_members/teams", locals: {project: @project}

View file

@ -1,20 +1,29 @@
%h3.page_title= "Edit Team #{@team.name}"
%hr
= form_for @team, url: team_path(@team) do |f|
- if @team.errors.any?
.alert.alert-error
%span= @team.errors.full_messages.first
.clearfix
= f.label :name do
Team name is
.input
= f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left"
.row
.span7
= form_for @team, url: team_path(@team) do |f|
- if @team.errors.any?
.alert.alert-error
%span= @team.errors.full_messages.first
.clearfix
= f.label :name do
Team name is
.input
= f.text_field :name, placeholder: "Ex. OpenSource", class: "xlarge left"
.clearfix
= f.label :path do
Team path is
.input
= f.text_field :path, placeholder: "opensource", class: "xlarge left"
.form-actions
= f.submit 'Save team changes', class: "btn btn-save"
.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"
.clearfix
= f.label :path do
Team path is
.input
= f.text_field :path, placeholder: "opensource", class: "xxlarge left"
.form-actions
= f.submit 'Save team changes', class: "btn btn-primary"
= link_to 'Delete team', team_path(@team), method: :delete, confirm: "You are shure?", class: "btn btn-remove pull-right"

View file

@ -10,22 +10,21 @@
%br
%small.cgray= user.email
.span6.pull-right
.span4
- if allow_admin
.left.span2
= 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"
.left.span2
%span
= check_box_tag :group_admin, true, @team.admin?(user)
Admin access
.pull-right
- if current_user == user
%span.btn.disabled This is you!
- if @team.owner == user
%span.btn.disabled.btn-success Owner
- elsif user.blocked
%span.btn.disabled.blocked Blocked
- elsif allow_admin
= link_to team_member_path(@team, user), confirm: remove_from_user_team_message(@team, user), method: :delete, class: "btn-tiny btn btn-remove" do
%i.icon-minus.icon-white
= 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 trigger-submit"
%br
= label_tag do
= f.check_box :group_admin, class: 'trigger-submit'
%span Admin access
.pull-right
- if current_user == user
%span.btn.disabled This is you!
- if @team.owner == user
%span.btn.disabled Owner
- elsif user.blocked
%span.btn.disabled.blocked Blocked
- elsif allow_admin
= link_to team_member_path(@team, user), confirm: remove_from_user_team_message(@team, user), method: :delete, class: "btn-tiny btn btn-remove" do
%i.icon-minus.icon-white

View file

@ -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 People within a team see only projects they have access to
%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

View file

@ -21,14 +21,18 @@ class PostReceive
return false
end
# Ignore push from non-gitlab users
user = if identifier.nil?
raise identifier.inspect
user = if identifier.blank?
# Local push from gitlab
email = project.repository.commit(newrev).author.email rescue nil
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 =~ /key/
elsif identifier =~ /\Auser-\d+\Z/
# 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.find_by_id(key_id).try(:user)
end