Commit 76f2e065 authored by Sytse Sijbrandij's avatar Sytse Sijbrandij

Merge branch 'master' into install-guide-improvements

Conflicts:
	doc/install/installation.md
parents 8ccad7c3 48d37272
......@@ -24,6 +24,7 @@ v 5.3.0
- init.d: Ensure socket is removed before starting service
- Admin area: Style teams:index, group:show pages
- Own page for failed forking
- Scrum view for milestone
v 5.2.0
- Turbolinks
......
......@@ -29,7 +29,7 @@ gem 'gitlab_git', '~> 1.3.0'
gem 'gitlab-grack', '~> 1.0.1', require: 'grack'
# LDAP Auth
gem 'gitlab_omniauth-ldap', '1.0.2', require: "omniauth-ldap"
gem 'gitlab_omniauth-ldap', '1.0.3', require: "omniauth-ldap"
# Syntax highlighter
gem "gitlab-pygments.rb", '~> 0.3.2', require: 'pygments.rb'
......@@ -72,6 +72,9 @@ gem "seed-fu"
gem "redcarpet", "~> 2.2.2"
gem "github-markup", "~> 0.7.4", require: 'github/markup'
# Asciidoc to HTML
gem "asciidoctor"
# Servers
gem "puma", '~> 2.0.1'
......
......@@ -46,6 +46,7 @@ GEM
rails (~> 3.0)
addressable (2.3.4)
arel (3.0.2)
asciidoctor (0.1.3)
awesome_print (1.1.0)
backports (2.6.7)
bcrypt-ruby (3.0.1)
......@@ -168,8 +169,8 @@ GEM
github-linguist (~> 2.3.4)
gitlab-grit (~> 2.5.1)
gitlab_meta (5.0)
gitlab_omniauth-ldap (1.0.2)
net-ldap (~> 0.2.2)
gitlab_omniauth-ldap (1.0.3)
net-ldap (~> 0.3.1)
omniauth (~> 1.0)
pyu-ruby-sasl (~> 0.0.3.1)
rubyntlm (~> 0.1.1)
......@@ -265,7 +266,7 @@ GEM
multi_xml (0.5.3)
multipart-post (1.2.0)
mysql2 (0.3.11)
net-ldap (0.2.2)
net-ldap (0.3.1)
nokogiri (1.5.9)
oauth (0.4.7)
oauth2 (0.8.1)
......@@ -517,6 +518,7 @@ PLATFORMS
DEPENDENCIES
acts-as-taggable-on
annotate!
asciidoctor
awesome_print
better_errors
binding_of_caller
......@@ -544,7 +546,7 @@ DEPENDENCIES
gitlab-pygments.rb (~> 0.3.2)
gitlab_git (~> 1.3.0)
gitlab_meta (= 5.0)
gitlab_omniauth-ldap (= 1.0.2)
gitlab_omniauth-ldap (= 1.0.3)
gon
grape (~> 0.4.1)
grape-entity (~> 0.3.0)
......
$ ->
$('.milestone-issue-filter li[data-closed]').addClass('hide')
$('.milestone-issue-filter ul.nav li a').click ->
$('.milestone-issue-filter li').toggleClass('active')
$('.milestone-issue-filter li[data-closed]').toggleClass('hide')
false
$('.milestone-merge-requests-filter li[data-closed]').addClass('hide')
$('.milestone-merge-requests-filter ul.nav li a').click ->
$('.milestone-merge-requests-filter li').toggleClass('active')
$('.milestone-merge-requests-filter li[data-closed]').toggleClass('hide')
false
......@@ -57,6 +57,7 @@
border-color: #CCC;
border-bottom: 1px solid #fff;
color: #333;
font-weight: bold;
}
}
}
......
......@@ -58,6 +58,7 @@
background: #f9f9f9;
border-radius: 0;
color: #555;
margin: 0 20px;
}
.note-file-attach {
......
......@@ -92,6 +92,10 @@ ul.notes {
.note-body {
@include md-typography;
margin-left: 45px;
.highlight {
@include border-radius(4px);
}
}
.note-header {
padding-bottom: 5px;
......
......@@ -8,7 +8,7 @@ module Issues
@issues = case params[:status]
when issues_filter[:all] then @project.issues
when issues_filter[:closed] then @project.issues.closed
when issues_filter[:to_me] then @project.issues.assigned(current_user)
when issues_filter[:to_me] then @project.issues.assigned_to(current_user)
when issues_filter[:by_me] then @project.issues.authored(current_user)
else @project.issues.opened
end
......
......@@ -51,6 +51,7 @@ module Projects
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?
@project.imported = true
true
else
@project.errors.add(:import_url, 'cannot clone repo')
......
......@@ -8,10 +8,6 @@ class Admin::GroupsController < Admin::ApplicationController
end
def show
@projects = Project.scoped
@projects = @projects.not_in_group(@group) if @group.projects.present?
@projects = @projects.all
@projects.reject!(&:empty_repo?)
end
def new
......
......@@ -55,8 +55,14 @@ class Admin::UsersController < Admin::ApplicationController
def create
admin = params[:user].delete("admin")
@admin_user = User.new(params[:user], as: :admin)
opts = {
force_random_password: true,
password_expires_at: Time.now
}
@admin_user = User.new(params[:user].merge(opts), as: :admin)
@admin_user.admin = (admin && admin.to_i > 0)
@admin_user.created_by_id = current_user.id
respond_to do |format|
if @admin_user.save
......
class ApplicationController < ActionController::Base
before_filter :authenticate_user!
before_filter :reject_blocked!
before_filter :check_password_expiration
before_filter :set_current_user_for_thread
before_filter :add_abilities
before_filter :dev_tools if Rails.env == 'development'
......@@ -156,4 +157,10 @@ class ApplicationController < ActionController::Base
gon.gravatar_url = request.ssl? || Gitlab.config.gitlab.https ? Gitlab.config.gravatar.ssl_url : Gitlab.config.gravatar.plain_url
gon.relative_url_root = Gitlab.config.gitlab.relative_url_root
end
def check_password_expiration
if current_user && current_user.password_expires_at && current_user.password_expires_at < Time.now
redirect_to new_profile_password_path and return
end
end
end
class PasswordsController < ApplicationController
layout 'navless'
skip_before_filter :check_password_expiration
before_filter :set_user
before_filter :set_title
def new
end
def create
new_password = params[:user][:password]
new_password_confirmation = params[:user][:password_confirmation]
result = @user.update_attributes(
password: new_password,
password_confirmation: new_password_confirmation
)
if result
@user.update_attributes(password_expires_at: nil)
redirect_to root_path, notice: 'Password successfully changed'
else
render :new
end
end
private
def set_user
@user = current_user
end
def set_title
@title = "New password"
end
end
......@@ -7,8 +7,12 @@ class SnippetsController < ApplicationController
# Allow destroy snippet
before_filter :authorize_admin_snippet!, only: [:destroy]
before_filter :set_title
respond_to :html
layout 'navless'
def index
@snippets = Snippet.public.fresh.non_expired.page(params[:page]).per(20)
end
......@@ -98,4 +102,8 @@ class SnippetsController < ApplicationController
def authorize_admin_snippet!
return render_404 unless can?(current_user, :admin_personal_snippet, @snippet)
end
def set_title
@title = 'Snippets'
end
end
......@@ -142,7 +142,7 @@ module ApplicationHelper
end
def user_color_scheme_class
COLOR_SCHEMES[current_user.try(:color_scheme_id)]
COLOR_SCHEMES[current_user.try(:color_scheme_id)] if defined?(current_user)
end
# Define whenever show last push event
......
......@@ -48,4 +48,36 @@ module ProjectsHelper
def remove_project_message(project)
"You are going to remove #{project.name_with_namespace}.\n Removed project CANNOT be restored!\n Are you ABSOLUTELY sure?"
end
def project_nav_tabs
@nav_tabs ||= get_project_nav_tabs(@project, current_user)
end
def project_nav_tab?(name)
project_nav_tabs.include? name
end
private
def get_project_nav_tabs(project, current_user)
nav_tabs = [:home]
if project.repo_exists? && can?(current_user, :download_code, project)
nav_tabs << [:files, :commits, :network, :graphs]
end
if project.repo_exists? && project.merge_requests_enabled
nav_tabs << :merge_requests
end
if can?(current_user, :admin_project, project)
nav_tabs << :settings
end
[:issues, :wiki, :wall, :snippets].each do |feature|
nav_tabs << feature if project.send :"#{feature}_enabled"
end
nav_tabs.flatten
end
end
......@@ -22,8 +22,10 @@ module Issuable
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)}
scope :assigned_to, ->(u) { where(assignee_id: u.id)}
scope :recent, -> { order("created_at DESC") }
scope :assigned, -> { where("assignee_id IS NOT NULL") }
scope :unassigned, -> { where("assignee_id IS NULL") }
delegate :name,
:email,
......
......@@ -27,7 +27,7 @@ class Issue < ActiveRecord::Base
scope :cared, ->(user) { where(assignee_id: user) }
scope :authored, ->(user) { where(author_id: user) }
scope :open_for, ->(user) { opened.assigned(user) }
scope :open_for, ->(user) { opened.assigned_to(user) }
state_machine :state, initial: :opened do
event :close do
......
......@@ -91,6 +91,15 @@ class MergeRequest < ActiveRecord::Base
if target_branch == source_branch
errors.add :branch_conflict, "You can not use same branch for source and target branches"
end
if opened? || reopened?
similar_mrs = self.project.merge_requests.where(source_branch: source_branch, target_branch: target_branch).opened
similar_mrs = similar_mrs.where('id not in (?)', self.id) if self.id
if similar_mrs.any?
errors.add :base, "There is already an open merge request for this branches"
end
end
end
def reload_code
......
......@@ -27,6 +27,7 @@ class Namespace < ActiveRecord::Base
message: "only letters, digits, spaces & '_' '-' '.' allowed." }
validates :description, length: { within: 0..255 }
validates :path, uniqueness: true, presence: true, length: { within: 1..255 },
exclusion: { in: Gitlab::Blacklist.path },
format: { with: Gitlab::Regex.path_regex,
message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
......
......@@ -79,6 +79,7 @@ class Project < ActiveRecord::Base
format: { with: Gitlab::Regex.project_name_regex,
message: "only letters, digits, spaces & '_' '-' '.' allowed. Letter should be first" }
validates :path, presence: true, length: { within: 0..255 },
exclusion: { in: Gitlab::Blacklist.path },
format: { with: Gitlab::Regex.path_regex,
message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
validates :issues_enabled, :wall_enabled, :merge_requests_enabled,
......@@ -92,7 +93,7 @@ class Project < ActiveRecord::Base
format: { with: URI::regexp(%w(http https)), message: "should be a valid url" },
if: :import?
validate :check_limit, :repo_name
validate :check_limit
# Scopes
scope :without_user, ->(user) { where("projects.id NOT IN (:ids)", ids: user.authorized_projects.map(&:id) ) }
......@@ -166,14 +167,6 @@ class Project < ActiveRecord::Base
errors[:base] << ("Can't check your ability to create project")
end
def repo_name
denied_paths = %w(admin dashboard groups help profile projects search)
if denied_paths.include?(path)
errors.add(:path, "like #{path} is not allowed")
end
end
def to_param
if namespace
namespace.path + "/" + path
......@@ -420,6 +413,10 @@ class Project < ActiveRecord::Base
!(forked_project_link.nil? || forked_project_link.forked_from_project.nil?)
end
def imported?
imported
end
def rename_repo
old_path_with_namespace = File.join(namespace_dir, path_was)
new_path_with_namespace = File.join(namespace_dir, path)
......
......@@ -42,8 +42,11 @@ class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation, :remember_me, :bio, :name, :username,
:skype, :linkedin, :twitter, :color_scheme_id, :theme_id, :force_random_password,
:extern_uid, :provider, as: [:default, :admin]
attr_accessible :projects_limit, :can_create_team, :can_create_group, as: :admin
:extern_uid, :provider, :password_expires_at,
as: [:default, :admin]
attr_accessible :projects_limit, :can_create_team, :can_create_group,
as: :admin
attr_accessor :force_random_password
......@@ -104,6 +107,7 @@ class User < ActiveRecord::Base
validates :extern_uid, allow_blank: true, uniqueness: {scope: :provider}
validates :projects_limit, presence: true, numericality: {greater_than_or_equal_to: 0}
validates :username, presence: true, uniqueness: true,
exclusion: { in: Gitlab::Blacklist.path },
format: { with: Gitlab::Regex.username_regex,
message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
......@@ -363,4 +367,8 @@ class User < ActiveRecord::Base
def accessible_deploy_keys
DeployKey.in_projects(self.master_projects).uniq
end
def created_by
User.find_by_id(created_by_id) if created_by_id
end
end
class ProjectObserver < BaseObserver
def after_create(project)
unless project.forked?
GitlabShellWorker.perform_async(
:add_repository,
project.path_with_namespace
)
log_info("#{project.owner.name} created a new project \"#{project.name_with_namespace}\"")
end
return true if project.forked? || project.imported?
GitlabShellWorker.perform_async(
:add_repository,
project.path_with_namespace
)
log_info("#{project.owner.name} created a new project \"#{project.name_with_namespace}\"")
end
def after_update(project)
......
%h3.page_title
Team: #{@team.name}
%fieldset
%legend Members (#{@team.members.count})
= form_tag admin_team_members_path(@team), id: "team_members", class: "bulk_import", method: :post do
%table#members_list
%thead
%tr
%th User name
%th Default project access
%th Team access
%th
- @team.members.each do |member|
%tr.member
%td
= link_to [:admin, member] do
= member.name
%small= "(#{member.email})"
%td= @team.human_default_projects_access(member)
%td= @team.admin?(member) ? "Admin" : "Member"
%td
%tr
%td= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name_with_username), multiple: true, data: {placeholder: 'Select users'}, class: 'chosen span5'
%td= select_tag :default_project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3" }
%td
%span= check_box_tag :group_admin
%span Admin?
%td= submit_tag 'Add', class: "btn btn-primary", id: :add_members_to_team
New members for
= link_to @team.name, admin_team_path(@team)
team
%hr
= form_tag admin_team_members_path(@team), id: "team_members", class: "bulk_import", method: :post do
- if @team.errors.any?
.alert.alert-error
%span= @team.errors.full_messages.first
.clearfix
= label_tag :user_ids do
Users to add
.input
= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name_with_username), multiple: true, data: {placeholder: 'Select users'}, class: 'chosen span5'
.clearfix.group-description-holder
= label_tag :default_project_access do
Default permission in projects
.input
= select_tag :default_project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3" }
.clearfix
= label_tag :group_admin do
Is team admin
.input
= check_box_tag :group_admin
.clearfix.form-actions
= submit_tag 'Add users into team', class: "btn btn-primary", id: :add_members_to_team
= link_to 'Cancel', :back, class: "btn"
......@@ -24,19 +24,25 @@
= f.text_field :email, required: true, autocomplete: "off"
%span.help-inline * required
%fieldset
%legend Password
.clearfix
= f.label :password
.input= f.password_field :password, disabled: f.object.force_random_password
.clearfix
= f.label :password_confirmation
.input= f.password_field :password_confirmation, disabled: f.object.force_random_password
-if f.object.new_record?
- if @admin_user.new_record?
%fieldset
%legend Password
.clearfix
= f.label :password
.input
%strong
A temporary password will be generated and sent to user.
%br
User will be forced to change it after first sign in
- else
%fieldset
%legend Password
.clearfix
= f.label :password
.input= f.password_field :password, disabled: f.object.force_random_password
.clearfix
= f.label :force_random_password do
%span Generate random password
.input= f.check_box :force_random_password, {}, true, nil
= f.label :password_confirmation
.input= f.password_field :password_confirmation, disabled: f.object.force_random_password
%fieldset
%legend Access
......
%h3.page_title
User:
= @admin_user.name
- if @admin_user.blocked?
%span.cred (Blocked)
- if @admin_user.admin
%span.cred (Admin)
.pull-right
= link_to edit_admin_user_path(@admin_user), class: "btn grouped btn-small" do
%i.icon-edit
Edit
- unless @admin_user == current_user
- if @admin_user.blocked?
= link_to 'Unblock', unblock_admin_user_path(@admin_user), method: :put, class: "btn grouped btn-small success"
- else
= link_to 'Block', block_admin_user_path(@admin_user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn grouped btn-small btn-remove"
= link_to 'Destroy', [:admin, @admin_user], confirm: "USER #{@admin_user.name} WILL BE REMOVED! Are you sure?", method: :delete, class: "btn grouped btn-small btn-remove"
%hr
.row
.span6
%h3.page_title
= image_tag gravatar_icon(@admin_user.email, 90), class: "avatar s90"
= @admin_user.name
- if @admin_user.blocked?
%span.cred (Blocked)
- if @admin_user.admin
%span.cred (Admin)
.pull-right
= link_to edit_admin_user_path(@admin_user), class: "btn pull-right" do
%i.icon-edit
Edit
%br
%small @#{@admin_user.username}
%br
%small member since #{@admin_user.created_at.stamp("Nov 12, 2031")}
.clearfix
%hr
%p
%span.btn.btn-small
%i.icon-envelope
= mail_to @admin_user.email
- unless @admin_user == current_user
- if @admin_user.blocked?
= link_to 'Unblock', unblock_admin_user_path(@admin_user), method: :put, class: "btn btn-small success"
- else
= link_to 'Block', block_admin_user_path(@admin_user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn btn-small btn-remove"
= link_to 'Destroy', [:admin, @admin_user], confirm: "USER #{@admin_user.name} WILL BE REMOVED! Are you sure?", method: :delete, class: "btn btn-small btn-remove"
.ui-box
%h5.title
Account:
.pull-right
= image_tag gravatar_icon(@admin_user.email, 32), class: "avatar s32"
%ul.well-list
%li
%span.light Name:
%strong= @admin_user.name
%li
%span.light Username:
%strong
= @admin_user.username
%li
%span.light Email:
%strong
= mail_to @admin_user.email
%li
%span.light Member since:
%strong
= @admin_user.created_at.stamp("Nov 12, 2031")
%li
%span.light Last sign-in at:
%strong
- if @admin_user.last_sign_in_at
= @admin_user.last_sign_in_at.stamp("Nov 12, 2031")
- else
never
- if @admin_user.ldap_user?
%li
%span.light LDAP uid:
%strong
= @admin_user.extern_uid
- if @admin_user.created_by
%li
%span.light Created by:
%strong
= link_to @admin_user.created_by.name, [:admin, @admin_user.created_by]
%hr
%h5
Add User to Projects
......@@ -67,11 +103,11 @@
.span6
= render 'users/profile', user: @admin_user
.ui-box
%h5.title Projects (#{@projects.count})
%ul.well-list
- @projects.sort_by(&:name_with_namespace).each do |project|
- tm = project.team.get_tm(@admin_user.id)
%li
= link_to admin_project_path(project), class: dom_class(project) do
- if project.namespace
......@@ -79,16 +115,17 @@
\/
%strong.well-title
= truncate(project.name, length: 45)
%span.pull-right.light
- if project.owner == @admin_user
%i.icon-wrench
- tm = project.team.get_tm(@admin_user.id)
- if tm
= tm.project_access_human
= link_to edit_admin_project_member_path(project, tm.user), class: "btn btn-small" do
- if project.owner == @admin_user
%span.label.label-info owner
- if tm
.pull-right
= link_to edit_admin_project_member_path(project, tm.user), class: "btn grouped btn-small" do
%i.icon-edit
= link_to admin_project_member_path(project, tm.user), confirm: remove_from_project_team_message(project, @admin_user), method: :delete, class: "btn btn-small btn-remove" do
= link_to admin_project_member_path(project, tm.user), confirm: remove_from_project_team_message(project, @admin_user), method: :delete, class: "btn grouped btn-small btn-remove" do
%i.icon-remove
%p.light
%i.icon-wrench
&ndash; user is a project owner
.pull-right.light
= tm.project_access_human
&nbsp;
- if enabled_oauth_providers.present?
- providers = (enabled_oauth_providers - [:ldap])
- if providers.present?
%hr
%div{:'data-no-turbolink' => 'data-no-turbolink'}
%span Sign in with: &nbsp;
- (enabled_oauth_providers - [:ldap]).each do |provider|
- providers.each do |provider|
%span
- if default_providers.include?(provider)
= link_to authbutton(provider, 32), omniauth_authorize_path(resource_name, provider)
......
......@@ -23,7 +23,7 @@
= hidden_field_tag 'last_commit', @last_commit
= hidden_field_tag 'content', '', id: :file_content
.commit-button-annotation
= button_tag "Commit", class: 'btn commit-btn js-commit-button'
= button_tag "Commit changes", class: 'btn commit-btn js-commit-button btn-primary'
.message
to branch
%strong= @ref
......