Skip to content
Snippets Groups Projects
Commit 02a29d1d authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets
Browse files

Merge branch '4-0-stable' into stable

parents 319f0c30 6a932d0a
No related branches found
No related tags found
No related merge requests found
Showing
with 522 additions and 162 deletions
Loading
Loading
@@ -21,7 +21,11 @@ class TeamMembersController < ProjectResourceController
params[:project_access]
)
 
redirect_to project_team_index_path(@project)
if params[:redirect_to]
redirect_to params[:redirect_to]
else
redirect_to project_team_index_path(@project)
end
end
 
def update
Loading
Loading
Loading
Loading
@@ -76,7 +76,7 @@ class CommitDecorator < ApplicationDecorator
source_name = send "#{options[:source]}_name".to_sym
source_email = send "#{options[:source]}_email".to_sym
text = if options[:avatar]
avatar = h.image_tag h.gravatar_icon(source_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size]
avatar = h.image_tag h.gravatar_icon(source_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: ""
%Q{#{avatar} <span class="commit-#{options[:source]}-name">#{source_name}</span>}
else
source_name
Loading
Loading
require 'digest/md5'
require 'uri'
 
module ApplicationHelper
 
Loading
Loading
@@ -30,13 +31,15 @@ module ApplicationHelper
args.any? { |v| v.to_s.downcase == action_name }
end
 
def gravatar_icon(user_email = '', size = 40)
if Gitlab.config.disable_gravatar? || user_email.blank?
def gravatar_icon(user_email = '', size = nil)
size = 40 if size.nil? || size <= 0
if !Gitlab.config.gravatar.enabled || user_email.blank?
'no_avatar.png'
else
gravatar_prefix = request.ssl? ? "https://secure" : "http://www"
gravatar_url = request.ssl? ? Gitlab.config.gravatar.ssl_url : Gitlab.config.gravatar.plain_url
user_email.strip!
"#{gravatar_prefix}.gravatar.com/avatar/#{Digest::MD5.hexdigest(user_email.downcase)}?s=#{size}&d=mm"
sprintf(gravatar_url, {:hash => Digest::MD5.hexdigest(user_email.downcase), :email => URI.escape(user_email), :size => size})
end
end
 
Loading
Loading
@@ -45,7 +48,7 @@ module ApplicationHelper
end
 
def web_app_url
"#{request_protocol}://#{Gitlab.config.web_host}/"
"#{request_protocol}://#{Gitlab.config.gitlab.host}/"
end
 
def last_commit(project)
Loading
Loading
@@ -75,7 +78,7 @@ module ApplicationHelper
end
 
def search_autocomplete_source
projects = current_user.projects.map{ |p| { label: p.name, url: project_path(p) } }
projects = current_user.projects.map{ |p| { label: p.name_with_namespace, url: project_path(p) } }
 
default_nav = [
{ label: "My Profile", url: profile_path },
Loading
Loading
@@ -92,6 +95,7 @@ module ApplicationHelper
{ label: "API Help", url: help_api_path },
{ label: "Markdown Help", url: help_markdown_path },
{ label: "SSH Keys Help", url: help_ssh_path },
{ label: "Gitlab Rake Tasks Help", url: help_raketasks_path },
]
 
project_nav = []
Loading
Loading
@@ -126,6 +130,10 @@ module ApplicationHelper
Gitlab::Theme.css_class_by_id(current_user.try(:theme_id))
end
 
def user_color_scheme_class
current_user.dark_scheme ? :black : :white
end
def show_last_push_widget?(event)
event &&
event.last_push_to_non_root? &&
Loading
Loading
module DashboardHelper
def dashboard_filter_path(entity, options={})
exist_opts = {
status: params[:status],
project_id: params[:project_id],
}
options = exist_opts.merge(options)
case entity
when 'issue' then
dashboard_issues_path(options)
when 'merge_request'
dashboard_merge_requests_path(options)
end
end
def entities_per_project project, entity
items = project.items_for(entity)
items = case params[:status]
when 'closed'
items.closed
when 'all'
items
else
items.opened
end
items.where(assignee_id: current_user.id).count
end
end
Loading
Loading
@@ -4,28 +4,6 @@ module IssuesHelper
project_issues_path project, params
end
 
def link_to_issue_assignee(issue)
project = issue.project
tm = project.team_member_by_id(issue.assignee_id)
if tm
link_to issue.assignee_name, project_team_member_path(project, tm), class: "author_link"
else
issue.assignee_name
end
end
def link_to_issue_author(issue)
project = issue.project
tm = project.team_member_by_id(issue.author_id)
if tm
link_to issue.author_name, project_team_member_path(project, tm), class: "author_link"
else
issue.author_name
end
end
def issue_css_classes issue
classes = "issue"
classes << " closed" if issue.closed
Loading
Loading
@@ -52,4 +30,14 @@ module IssuesHelper
open: "open"
}
end
def labels_autocomplete_source
labels = @project.issues_labels.order('count DESC')
labels = labels.map{ |l| { label: l.name, value: l.name } }
labels.to_json
end
def issues_active_milestones
@project.milestones.active.order("id desc").all
end
end
module MergeRequestsHelper
def link_to_merge_request_assignee(merge_request)
project = merge_request.project
tm = project.team_member_by_id(merge_request.assignee_id)
if tm
link_to merge_request.assignee_name, project_team_member_path(project, tm), class: "author_link"
else
merge_request.assignee_name
end
end
def link_to_merge_request_author(merge_request)
project = merge_request.project
tm = project.team_member_by_id(merge_request.author_id)
if tm
link_to merge_request.author_name, project_team_member_path(project, tm), class: "author_link"
else
merge_request.author_name
end
end
def new_mr_path_from_push_event(event)
new_project_merge_request_path(
event.project,
Loading
Loading
@@ -39,7 +17,7 @@ module MergeRequestsHelper
classes
end
 
def ci_status_path
@project.gitlab_ci_service.commit_badge_path(@merge_request.last_commit.sha)
def ci_build_details_path merge_request
merge_request.project.gitlab_ci_service.build_page(merge_request.last_commit.sha)
end
end
module NamespacesHelper
def namespaces_options(selected = :current_user, scope = :default)
groups = current_user.namespaces.select {|n| n.type == 'Group'}
users = if scope == :all
Namespace.root
else
current_user.namespaces.reject {|n| n.type == 'Group'}
end
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]} ]
options = []
options << global_opts if current_user.admin
options << group_opts
options << users_opts
if selected == :current_user && current_user.namespace
selected = current_user.namespace.id
end
grouped_options_for_select(options, selected)
end
end
Loading
Loading
@@ -8,11 +8,49 @@ module ProjectsHelper
end
 
def link_to_project project
link_to project.name, project
link_to project do
title = content_tag(:strong, project.name)
if project.namespace
namespace = content_tag(:span, "#{project.namespace.human_name} / ", class: 'tiny')
title = namespace + title
end
title
end
end
def link_to_member(project, author)
return "(deleted)" unless author
# Build avatar image tag
avatar = image_tag(gravatar_icon(author.try(:email)), width: 16, class: "lil_av")
# Build name strong tag
name = content_tag :strong, author.name, class: 'author'
author_html = avatar + name
tm = project.team_member_by_id(author)
content_tag :span, class: 'member-link' do
if tm
link_to author_html, project_team_member_path(project, tm), class: "author_link"
else
author_html
end
end
end
 
def tm_path team_member
project_team_member_path(@project, team_member)
end
end
 
def project_title project
if project.group
project.name_with_namespace
else
project.name
end
end
end
Loading
Loading
@@ -72,7 +72,7 @@ module TabHelper
return "active" if current_page?(controller: "projects", action: action, id: @project)
end
 
if ['snippets', 'hooks', 'deploy_keys', 'team_members'].include? controller.controller_name
if ['snippets', 'services', 'hooks', 'deploy_keys', 'team_members'].include? controller.controller_name
"active"
end
end
Loading
Loading
@@ -84,4 +84,17 @@ module TabHelper
'active'
end
end
# Use nav_tab for save controller/action but different params
def nav_tab key, value, &block
o = {}
o[:class] = ""
o[:class] << " active" if params[key] == value
if block_given?
content_tag(:li, capture(&block), o)
else
content_tag(:li, nil, o)
end
end
end
Loading
Loading
@@ -3,11 +3,11 @@ class Notify < ActionMailer::Base
add_template_helper ApplicationHelper
add_template_helper GitlabMarkdownHelper
 
default_url_options[:host] = Gitlab.config.web_host
default_url_options[:protocol] = Gitlab.config.web_protocol
default_url_options[:port] = Gitlab.config.web_port if Gitlab.config.web_custom_port?
default_url_options[:host] = Gitlab.config.gitlab.host
default_url_options[:protocol] = Gitlab.config.gitlab.protocol
default_url_options[:port] = Gitlab.config.gitlab.port if Gitlab.config.gitlab_on_non_standard_port?
 
default from: Gitlab.config.email_from
default from: Gitlab.config.gitlab.email_from
 
 
 
Loading
Loading
@@ -31,6 +31,7 @@ class Notify < ActionMailer::Base
def issue_status_changed_email(recipient_id, issue_id, status, updated_by_user_id)
@issue = Issue.find issue_id
@issue_status = status
@project = @issue.project
@updated_by = User.find updated_by_user_id
mail(to: recipient(recipient_id),
subject: subject("changed issue ##{@issue.id}", @issue.title))
Loading
Loading
@@ -89,14 +90,6 @@ class Notify < ActionMailer::Base
mail(to: recipient(recipient_id), subject: subject)
end
 
def note_wiki_email(recipient_id, note_id)
@note = Note.find(note_id)
@wiki = @note.noteable
@project = @note.project
mail(to: recipient(recipient_id), subject: subject("note for wiki"))
end
 
#
# Project
Loading
Loading
@@ -105,11 +98,17 @@ class Notify < ActionMailer::Base
def project_access_granted_email(user_project_id)
@users_project = UsersProject.find user_project_id
@project = @users_project.project
mail(to: @users_project.user.email,
mail(to: @users_project.user.email,
subject: subject("access to project was granted"))
end
 
 
def project_was_moved_email(user_project_id)
@users_project = UsersProject.find user_project_id
@project = @users_project.project
mail(to: @users_project.user.email,
subject: subject("project was moved"))
end
 
#
# User
Loading
Loading
Loading
Loading
@@ -7,6 +7,7 @@ class Ability
when "Note" then note_abilities(object, subject)
when "Snippet" then snippet_abilities(object, subject)
when "MergeRequest" then merge_request_abilities(object, subject)
when "Group" then group_abilities(object, subject)
else []
end
end
Loading
Loading
@@ -14,7 +15,40 @@ class Ability
def project_abilities(user, project)
rules = []
 
rules << [
# Rules based on role in project
if project.master_access_for?(user)
rules << project_master_rules
elsif project.dev_access_for?(user)
rules << project_dev_rules
elsif project.report_access_for?(user)
rules << project_report_rules
elsif project.guest_access_for?(user)
rules << project_guest_rules
end
if project.namespace
# If user own project namespace
# (Ex. group owner or account owner)
if project.namespace.owner == user
rules << project_admin_rules
end
else
# For compatibility with global projects
# use projects.owner_id
if project.owner == user
rules << project_admin_rules
end
end
rules.flatten
end
def project_guest_rules
[
:read_project,
:read_wiki,
:read_issue,
Loading
Loading
@@ -26,28 +60,30 @@ class Ability
:write_project,
:write_issue,
:write_note
] if project.guest_access_for?(user)
]
end
 
rules << [
def project_report_rules
project_guest_rules + [
:download_code,
:write_merge_request,
:write_snippet
] if project.report_access_for?(user)
]
end
 
rules << [
def project_dev_rules
project_report_rules + [
:write_wiki,
:push_code
] if project.dev_access_for?(user)
rules << [
:push_code_to_protected_branches
] if project.master_access_for?(user)
]
end
 
rules << [
def project_master_rules
project_dev_rules + [
:push_code_to_protected_branches,
:modify_issue,
:modify_snippet,
:modify_merge_request,
:admin_project,
:admin_issue,
:admin_milestone,
:admin_snippet,
Loading
Loading
@@ -55,8 +91,25 @@ class Ability
:admin_merge_request,
:admin_note,
:accept_mr,
:admin_wiki
] if project.master_access_for?(user) || project.owner == user
:admin_wiki,
:admin_project
]
end
def project_admin_rules
project_master_rules + [
:change_namespace,
:rename_project,
:remove_project
]
end
def group_abilities user, group
rules = []
rules << [
:manage_group
] if group.owner == user
 
rules.flatten
end
Loading
Loading
Loading
Loading
@@ -87,14 +87,10 @@ class Commit
last = project.commit(from.try(:strip))
 
if first && last
commits = [first, last].sort_by(&:created_at)
younger = commits.first
older = commits.last
result[:same] = (younger.id == older.id)
result[:commits] = project.repo.commits_between(younger.id, older.id).map {|c| Commit.new(c)}
result[:diffs] = project.repo.diff(younger.id, older.id) rescue []
result[:commit] = Commit.new(older)
result[:same] = (first.id == last.id)
result[:commits] = project.repo.commits_between(last.id, first.id).map {|c| Commit.new(c)}
result[:diffs] = project.repo.diff(last.id, first.id) rescue []
result[:commit] = Commit.new(first)
end
 
result
Loading
Loading
@@ -150,4 +146,21 @@ class Commit
def parents_count
parents && parents.count || 0
end
# Shows the diff between the commit's parent and the commit.
#
# Cuts out the header and stats from #to_patch and returns only the diff.
def to_diff
# see Grit::Commit#show
patch = to_patch
# discard lines before the diff
lines = patch.split("\n")
while !lines.first.start_with?("diff --git") do
lines.shift
end
lines.pop if lines.last =~ /^[\d.]+$/ # Git version
lines.pop if lines.last == "-- " # end of diff
lines.join("\n")
end
end
Loading
Loading
@@ -15,6 +15,7 @@
#
 
class Event < ActiveRecord::Base
include NoteEvent
include PushEvent
 
attr_accessible :project, :action, :data, :author_id, :project_id,
Loading
Loading
@@ -58,12 +59,14 @@ class Event < ActiveRecord::Base
end
end
 
# Next events currently enabled for system
# - push
# - new issue
# - merge request
def allowed?
push? || issue? || merge_request? || membership_changed?
def proper?
if push?
true
elsif membership_changed?
true
else
(issue? || merge_request? || note? || milestone?) && target
end
end
 
def project_name
Loading
Loading
@@ -94,6 +97,14 @@ class Event < ActiveRecord::Base
action == self.class::Reopened
end
 
def milestone?
target_type == "Milestone"
end
def note?
target_type == "Note"
end
def issue?
target_type == "Issue"
end
Loading
Loading
Loading
Loading
@@ -36,4 +36,22 @@ class GitlabCiService < Service
def commit_badge_path sha
project_url + "/status?sha=#{sha}"
end
def commit_status_path sha
project_url + "/builds/#{sha}/status.json?token=#{token}"
end
def commit_status sha
response = HTTParty.get(commit_status_path(sha))
if response.code == 200 and response["status"]
response["status"]
else
:error
end
end
def build_page sha
project_url + "/builds/#{sha}"
end
end
# == Schema Information
#
# Table name: groups
# Table name: namespaces
#
# id :integer not null, primary key
# name :string(255) not null
# code :string(255) not null
# path :string(255) not null
# owner_id :integer not null
# created_at :datetime not null
# updated_at :datetime not null
# type :string(255)
#
 
class Group < ActiveRecord::Base
attr_accessible :code, :name, :owner_id
has_many :projects
belongs_to :owner, class_name: "User"
validates :name, presence: true, uniqueness: true
validates :code, presence: true, uniqueness: true
validates :owner, presence: true
delegate :name, to: :owner, allow_nil: true, prefix: true
def self.search query
where("name LIKE :query OR code LIKE :query", query: "%#{query}%")
end
def to_param
code
class Group < Namespace
def users
users = User.joins(:users_projects).where(users_projects: {project_id: project_ids})
users = users << owner
users.uniq
end
 
def users
User.joins(:users_projects).where(users_projects: {project_id: project_ids}).uniq
def human_name
name
end
end
Loading
Loading
@@ -202,20 +202,26 @@ class MergeRequest < ActiveRecord::Base
false
end
 
def to_raw
FileUtils.mkdir_p(Rails.root.join("tmp", "patches"))
patch_path = Rails.root.join("tmp", "patches", "merge_request_#{self.id}.patch")
from = commits.last.id
to = source_branch
def mr_and_commit_notes
commit_ids = commits.map(&:id)
Note.where("(noteable_type = 'MergeRequest' AND noteable_id = :mr_id) OR (noteable_type = 'Commit' AND commit_id IN (:commit_ids))", mr_id: id, commit_ids: commit_ids)
end
 
project.repo.git.run('', "format-patch" , " > #{patch_path.to_s}", {}, ["#{from}..#{to}", "--stdout"])
# Returns the raw diff for this merge request
#
# see "git diff"
def to_diff
project.repo.git.native(:diff, {timeout: 30, raise: true}, "#{target_branch}...#{source_branch}")
end
 
patch_path
# Returns the commit as a series of email patches.
#
# see "git format-patch"
def to_patch
project.repo.git.format_patch({timeout: 30, raise: true, stdout: true}, "#{target_branch}..#{source_branch}")
end
 
def mr_and_commit_notes
commit_ids = commits.map(&:id)
Note.where("(noteable_type = 'MergeRequest' AND noteable_id = :mr_id) OR (noteable_type = 'Commit' AND noteable_id IN (:commit_ids))", mr_id: id, commit_ids: commit_ids)
def last_commit_short_sha
@last_commit_short_sha ||= last_commit.sha[0..10]
end
end
Loading
Loading
@@ -13,18 +13,26 @@
#
 
class Milestone < ActiveRecord::Base
attr_accessible :title, :description, :due_date, :closed
attr_accessible :title, :description, :due_date, :closed, :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)
validates :title, presence: true
validates :project, presence: true
validates :closed, inclusion: { in: [true, false] }
 
def self.active
where("due_date > ? OR due_date IS NULL", Date.today)
def expired?
if due_date
due_date < Date.today
else
false
end
end
 
def participants
Loading
Loading
@@ -52,4 +60,20 @@ class Milestone < ActiveRecord::Base
def expires_at
"expires at #{due_date.stamp("Aug 21, 2011")}" if due_date
end
def can_be_closed?
open? && 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
end
# == Schema Information
#
# Table name: namespaces
#
# id :integer not null, primary key
# name :string(255) not null
# path :string(255) not null
# owner_id :integer not null
# created_at :datetime not null
# updated_at :datetime not null
# type :string(255)
#
class Namespace < ActiveRecord::Base
attr_accessible :name, :path
has_many :projects, dependent: :destroy
belongs_to :owner, class_name: "User"
validates :name, presence: true, uniqueness: true
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
after_create :ensure_dir_exist
after_update :move_dir
after_destroy :rm_dir
scope :root, where('type IS NULL')
def self.search query
where("name LIKE :query OR path LIKE :query", query: "%#{query}%")
end
def self.global_id
'GLN'
end
def to_param
path
end
def human_name
owner_name
end
def ensure_dir_exist
namespace_dir_path = File.join(Gitlab.config.gitolite.repos_path, path)
system("mkdir -m 770 #{namespace_dir_path}") unless File.exists?(namespace_dir_path)
end
def move_dir
if path_changed?
old_path = File.join(Gitlab.config.gitolite.repos_path, path_was)
new_path = File.join(Gitlab.config.gitolite.repos_path, path)
if File.exists?(new_path)
raise "Already exists"
end
if system("mv #{old_path} #{new_path}")
send_update_instructions
end
end
end
def rm_dir
dir_path = File.join(Gitlab.config.gitolite.repos_path, path)
system("rm -rf #{dir_path}")
end
def send_update_instructions
projects.each(&:send_move_instructions)
end
end
Loading
Loading
@@ -20,7 +20,7 @@ require 'file_size_validator'
class Note < ActiveRecord::Base
 
attr_accessible :note, :noteable, :noteable_id, :noteable_type, :project_id,
:attachment, :line_code
:attachment, :line_code, :commit_id
 
attr_accessor :notify
attr_accessor :notify_author
Loading
Loading
@@ -32,14 +32,17 @@ class Note < ActiveRecord::Base
delegate :name, to: :project, prefix: true
delegate :name, :email, to: :author, prefix: true
 
validates :project, presence: true
validates :note, presence: true, length: { within: 0..5000 }
validates :note, :project, presence: true
validates :attachment, file_size: { maximum: 10.megabytes.to_i }
 
mount_uploader :attachment, AttachmentUploader
validates :noteable_id, presence: true, if: ->(n) { n.noteable_type.present? && n.noteable_type != 'Commit' }
validates :commit_id, presence: true, if: ->(n) { n.noteable_type == 'Commit' }
mount_uploader :attachment, AttachmentUploader
 
# Scopes
scope :common, ->{ where(noteable_id: nil) }
scope :for_commits, ->{ where(noteable_type: "Commit") }
scope :common, ->{ where(noteable_id: nil, commit_id: nil) }
scope :today, ->{ where("created_at >= :date", date: Date.today) }
scope :last_week, ->{ where("created_at >= :date", date: (Date.today - 7.days)) }
scope :since, ->(day) { where("created_at >= :date", date: (day)) }
Loading
Loading
@@ -67,7 +70,7 @@ class Note < ActiveRecord::Base
# override to return commits, which are not active record
def noteable
if for_commit?
project.commit(noteable_id)
project.commit(commit_id)
else
super
end
Loading
Loading
@@ -122,4 +125,12 @@ class Note < ActiveRecord::Base
def downvote?
note.start_with?('-1') || note.start_with?(':-1:')
end
def noteable_type_name
if noteable_type.present?
noteable_type.downcase
else
"wall"
end
end
end
Loading
Loading
@@ -9,14 +9,13 @@
# created_at :datetime not null
# updated_at :datetime not null
# private_flag :boolean default(TRUE), not null
# code :string(255)
# owner_id :integer
# default_branch :string(255)
# issues_enabled :boolean default(TRUE), not null
# wall_enabled :boolean default(TRUE), not null
# merge_requests_enabled :boolean default(TRUE), not null
# wiki_enabled :boolean default(TRUE), not null
# group_id :integer
# namespace_id :integer
#
 
require "grit"
Loading
Loading
@@ -26,13 +25,24 @@ class Project < ActiveRecord::Base
include PushObserver
include Authority
include Team
include NamespacedProject
class TransferError < StandardError; end
attr_accessible :name, :path, :description, :default_branch, :issues_enabled,
:wall_enabled, :merge_requests_enabled, :wiki_enabled, as: [:default, :admin]
attr_accessible :namespace_id, :owner_id, as: :admin
 
attr_accessible :name, :path, :description, :code, :default_branch, :issues_enabled,
:wall_enabled, :merge_requests_enabled, :wiki_enabled
attr_accessor :error_code
 
# Relations
belongs_to :group
belongs_to :group, foreign_key: "namespace_id", conditions: "type = 'Group'"
belongs_to :namespace
# TODO: replace owner with creator.
# With namespaces a project owner will be a namespace owner
# so this field makes sense only for global projects
belongs_to :owner, class_name: "User"
has_many :users, through: :users_projects
has_many :events, dependent: :destroy
Loading
Loading
@@ -54,36 +64,79 @@ class Project < ActiveRecord::Base
# Validations
validates :owner, presence: true
validates :description, length: { within: 0..2000 }
validates :name, uniqueness: true, presence: true, length: { within: 0..255 }
validates :path, uniqueness: true, presence: true, length: { within: 0..255 },
format: { with: /\A[a-zA-Z][a-zA-Z0-9_\-\.]*\z/,
message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
validates :code, presence: true, uniqueness: true, length: { within: 1..255 },
format: { with: /\A[a-zA-Z][a-zA-Z0-9_\-\.]*\z/,
validates :name, presence: true, length: { within: 0..255 }
validates :path, presence: true, length: { within: 0..255 },
format: { with: Gitlab::Regex.path_regex,
message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
validates :issues_enabled, :wall_enabled, :merge_requests_enabled,
:wiki_enabled, inclusion: { in: [true, false] }
validates_uniqueness_of :name, scope: :namespace_id
validates_uniqueness_of :path, scope: :namespace_id
validate :check_limit, :repo_name
 
# Scopes
scope :public_only, where(private_flag: false)
scope :without_user, ->(user) { where("id NOT IN (:ids)", ids: user.projects.map(&:id) ) }
scope :not_in_group, ->(group) { where("id NOT IN (:ids)", ids: group.project_ids ) }
scope :sorted_by_activity, ->() { order("(SELECT max(events.created_at) FROM events WHERE events.project_id = projects.id) DESC") }
scope :personal, ->(user) { where(namespace_id: user.namespace_id) }
scope :joined, ->(user) { where("namespace_id != ?", user.namespace_id) }
 
class << self
def authorized_for user
projects = includes(:users_projects, :namespace)
projects = projects.where("users_projects.user_id = :user_id or projects.owner_id = :user_id or namespaces.owner_id = :user_id", user_id: user.id)
end
def active
joins(:issues, :notes, :merge_requests).order("issues.created_at, notes.created_at, merge_requests.created_at DESC")
end
 
def search query
where("name LIKE :query OR code LIKE :query OR path LIKE :query", query: "%#{query}%")
where("projects.name LIKE :query OR projects.path LIKE :query", query: "%#{query}%")
end
def find_with_namespace(id)
if id.include?("/")
id = id.split("/")
namespace_id = Namespace.find_by_path(id.first).id
where(namespace_id: namespace_id).find_by_path(id.last)
else
where(path: id, namespace_id: nil).last
end
end
 
def create_by_user(params, user)
namespace_id = params.delete(:namespace_id)
project = Project.new params
 
Project.transaction do
# Parametrize path for project
#
# Ex.
# 'GitLab HQ'.parameterize => "gitlab-hq"
#
project.path = project.name.dup.parameterize
project.owner = user
# Apply namespace if user has access to it
# else fallback to user namespace
if namespace_id != Namespace.global_id
project.namespace_id = user.namespace_id
if namespace_id
group = Group.find_by_id(namespace_id)
if user.can? :manage_group, group
project.namespace_id = namespace_id
end
end
end
project.save!
 
# Add user as project master
Loading
Loading
@@ -126,7 +179,7 @@ class Project < ActiveRecord::Base
end
 
def repo_name
denied_paths = %w(gitolite-admin groups projects dashboard)
denied_paths = %w(gitolite-admin admin dashboard groups help profile projects search)
 
if denied_paths.include?(path)
errors.add(:path, "like #{path} is not allowed")
Loading
Loading
@@ -134,11 +187,15 @@ class Project < ActiveRecord::Base
end
 
def to_param
code
if namespace
namespace.path + "/" + path
else
path
end
end
 
def web_url
[Gitlab.config.url, code].join("/")
[Gitlab.config.gitlab.url, path_with_namespace].join("/")
end
 
def common_notes
Loading
Loading
@@ -146,15 +203,15 @@ class Project < ActiveRecord::Base
end
 
def build_commit_note(commit)
notes.new(noteable_id: commit.id, noteable_type: "Commit")
notes.new(commit_id: commit.id, noteable_type: "Commit")
end
 
def commit_notes(commit)
notes.where(noteable_id: commit.id, noteable_type: "Commit", line_code: nil)
notes.where(commit_id: commit.id, noteable_type: "Commit", line_code: nil)
end
 
def commit_line_notes(commit)
notes.where(noteable_id: commit.id, noteable_type: "Commit").where("line_code IS NOT NULL")
notes.where(commit_id: commit.id, noteable_type: "Commit").where("line_code IS NOT NULL")
end
 
def public?
Loading
Loading
@@ -173,10 +230,6 @@ class Project < ActiveRecord::Base
last_event.try(:created_at) || updated_at
end
 
def wiki_notes
Note.where(noteable_id: wikis.pluck(:id), noteable_type: 'Wiki', project_id: self.id)
end
def project_id
self.id
end
Loading
Loading
@@ -192,4 +245,24 @@ class Project < ActiveRecord::Base
def gitlab_ci?
gitlab_ci_service && gitlab_ci_service.active
end
# For compatibility with old code
def code
path
end
def items_for entity
case entity
when 'issue' then
issues
when 'merge_request' then
merge_requests
end
end
def send_move_instructions
self.users_projects.each do |member|
Notify.project_was_moved_email(member.id).deliver
end
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment