Commit 2a9eed66 authored by Vinnie Okada's avatar Vinnie Okada

Merge branch 'master' into rails-4.1.9

Conflicts:
	app/views/projects/commits/_commit.html.haml
	app/views/projects/issues/_issue.html.haml
	app/views/projects/issues/_issue_context.html.haml
	app/views/projects/merge_requests/_merge_request.html.haml
	app/views/projects/merge_requests/show/_context.html.haml
parents 83e2a1ca 71e14699
v 7.9.0 (unreleased)
- Fix broken access control for note attachments (Hannes Rosenögger)
- Move labels/milestones tabs to sidebar
- Improve UI for commits, issues and merge request lists
- Fix commit comments on first line of diff not rendering in Merge Request Discussion view.
v 7.8.0 (unreleased)
- Fix access control and protection against XSS for note attachments and other uploads.
- Replace highlight.js with rouge-fork rugments (Stefan Tatschner)
- Make project search case insensitive (Hannes Rosenögger)
- Include issue/mr participants in list of recipients for reassign/close/reopen emails
......@@ -64,6 +67,8 @@ v 7.8.0 (unreleased)
- Remove deprecated Group#owner_id from API
- Show projects user contributed to on user page. Show stars near project on user page.
- Improve database performance for GitLab
- Add Asana service (Jeremy Benoist)
- Improve project web hooks with extra data
v 7.7.2
- Update GitLab Shell to version 2.4.2 that fixes a bug when developers can push to protected branch
......
......@@ -16,11 +16,8 @@ class @calendar
subDomain: "day"
range: 12
tooltip: true
domainDynamicDimension: false
colLimit: 4
label:
position: "top"
domainMargin: 1
legend: [
0
1
......
.commit-title{
display: block;
}
.commit-title{
margin-bottom: 10px;
}
.commit-author, .commit-committer{
display: block;
color: #999;
font-weight: normal;
font-style: italic;
}
.commit-author strong, .commit-committer strong{
font-weight: bold;
font-style: normal;
}
.commit-description {
background: none;
border: none;
margin: 0;
padding: 0;
margin-top: 10px;
}
.commit-stat-summary {
color: #666;
font-size: 14px;
font-weight: normal;
padding: 10px 0;
}
.commit-info-row {
margin-bottom: 10px;
.avatar {
@extend .avatar-inline;
}
.commit-committer-link,
.commit-author-link {
color: #444;
font-weight: bold;
}
}
.commit-committer-link,
.commit-author-link {
font-size: 13px;
color: #555;
&:hover {
color: #999;
}
}
.commit-box {
margin: 10px 0;
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
padding: 20px 0;
.commit-title {
margin: 0;
}
.commit-description {
margin-top: 15px;
}
}
.file-stats a {
color: $style_color;
}
.file-stats {
.new-file {
a {
color: #090;
}
i {
color: #1BCF00;
}
}
.renamed-file {
i {
color: #FE9300;
}
}
.deleted-file {
a {
color: #B00;
}
i {
color: #EE0000;
}
}
.edit-file{
i{
color: #555;
}
}
}
/*
* Commit message textarea for web editor and
* custom merge request message
*/
.commit-message-container {
background-color: $body-bg;
position: relative;
font-family: $monospace_font;
$left: 12px;
.max-width-marker {
width: 72ch;
color: rgba(0, 0, 0, 0.0);
font-family: inherit;
left: $left;
height: 100%;
border-right: 1px solid mix($input-border, white);
position: absolute;
z-index: 1;
}
> textarea {
background-color: rgba(0, 0, 0, 0.0);
font-family: inherit;
padding-left: $left;
position: relative;
z-index: 2;
}
}
/**
* Commit file
*/
.commit-committer-link,
.commit-author-link {
font-size: 13px;
color: #555;
&:hover {
color: #999;
}
}
/** COMMIT BLOCK **/
.commit-title{
display: block;
}
.commit-title{
margin-bottom: 10px;
}
.commit-author, .commit-committer{
display: block;
color: #999;
font-weight: normal;
font-style: italic;
}
.commit-author strong, .commit-committer strong{
font-weight: bold;
font-style: normal;
}
.file-stats a {
color: $style_color;
}
.file-stats {
.new-file {
a {
color: #090;
}
i {
color: #1BCF00;
}
}
.renamed-file {
i {
color: #FE9300;
}
}
.deleted-file {
a {
color: #B00;
}
i {
color: #EE0000;
}
}
.edit-file{
i{
color: #555;
}
}
}
.label_commit {
@include border-radius(4px);
padding: 2px 4px;
font-size: 13px;
background: #474D57;
color: #fff;
font-family: $monospace_font;
}
.commits-compare-switch{
background: image-url("switch_icon.png") no-repeat center center;
width: 32px;
......@@ -85,60 +11,40 @@
background-color: #EEE;
}
.commit-description {
background: none;
border: none;
margin: 0;
padding: 0;
margin-top: 10px;
}
.commit-box {
.lists-separator {
margin: 10px 0;
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
padding: 20px 0;
border-color: #DDD;
}
.commit-title {
.commits-row {
ul {
margin: 0;
li.commit {
padding: 8px 0;
}
}
.commit-description {
margin-top: 15px;
.commits-row-date {
font-size: 15px;
line-height: 20px;
margin-bottom: 5px;
}
}
.commits-feed-holder {
float: right;
.commit-stat-summary {
color: #666;
font-size: 14px;
font-weight: normal;
padding: 10px 0;
}
.commit-info-row {
margin-bottom: 10px;
.avatar {
@extend .avatar-inline;
}
.commit-committer-link,
.commit-author-link {
color: #444;
font-weight: bold;
.btn {
padding: 4px 12px;
}
}
.lists-separator {
margin: 10px 0;
border-top: 1px dashed #CCC;
}
/**
* COMMIT ROW
*/
li.commit {
.commit-row-title {
font-size: $list-font-size;
line-height: 20px;
margin-bottom: 2px;
.notes_count {
......@@ -156,9 +62,9 @@ li.commit {
}
.commit-row-message {
color: #333;
color: #444;
&:hover {
color: #444;
text-decoration: underline;
}
}
......@@ -193,13 +99,14 @@ li.commit {
.commit-row-info {
color: #777;
line-height: 24px;
a {
color: #777;
}
.committed_ago {
float: right;
display: inline-block;
}
}
......@@ -214,34 +121,3 @@ li.commit {
}
}
}
.commits-feed-holder {
float: right;
.btn {
padding: 4px 12px;
}
}
.commit-message-container {
background-color: $body-bg;
position: relative;
font-family: $monospace_font;
$left: 12px;
.max-width-marker {
width: 72ch;
color: rgba(0, 0, 0, 0.0);
font-family: inherit;
left: $left;
height: 100%;
border-right: 1px solid mix($input-border, white);
position: absolute;
z-index: 1;
}
> textarea {
background-color: rgba(0, 0, 0, 0.0);
font-family: inherit;
padding-left: $left;
position: relative;
z-index: 2;
}
}
......@@ -23,3 +23,19 @@
}
}
}
.issuable-context-title {
font-size: 15px;
line-height: 1.4;
margin-bottom: 5px;
.avatar {
margin-left: 0;
}
label {
color: #666;
font-weight: normal;
margin-right: 4px;
}
}
......@@ -6,6 +6,7 @@
.issue-title {
margin-bottom: 5px;
font-size: $list-font-size;
font-weight: bold;
}
.issue-info {
......@@ -170,9 +171,9 @@ form.edit-issue {
}
}
h3.issue-title {
h2.issue-title {
margin-top: 0;
font-size: 2em;
font-weight: bold;
}
.context .select2-container {
......
......@@ -91,6 +91,7 @@
.merge-request-title {
margin-bottom: 5px;
font-size: $list-font-size;
font-weight: bold;
}
.merge-request-info {
......
......@@ -12,7 +12,6 @@
.sidebar-wrapper {
z-index: 99;
overflow-y: auto;
background: #F5F5F5;
}
......@@ -41,7 +40,7 @@
.nav-sidebar li {
&.active a {
color: #333;
background: #FFF;
background: #FFF !important;
font-weight: bold;
border: 1px solid #EEE;
border-right: 1px solid transparent;
......@@ -78,7 +77,7 @@
&:hover {
text-decoration: none;
color: #333;
background: #DDD;
background: #EEE;
}
&:active, &:focus {
......@@ -126,7 +125,6 @@
.sidebar-wrapper {
width: 52px;
overflow-x: hidden;
.nav-sidebar {
margin-top: 20px;
......@@ -140,6 +138,7 @@
padding: 8px 15px;
text-align: center;
& > span {
display: none;
}
......
......@@ -169,7 +169,7 @@
color: #999;
background: #FFF;
padding: 5px;
margin-top: -7px;
margin-top: -11px;
border: 1px solid #DDD;
font-size: 13px;
}
class Import::GitoriousController < Import::BaseController
def new
redirect_to client.authorize_url(callback_import_gitorious_url)
end
def callback
session[:gitorious_repos] = params[:repos]
redirect_to status_import_gitorious_url
end
def status
@repos = client.repos
@already_added_projects = current_user.created_projects.where(import_type: "gitorious")
already_added_projects_names = @already_added_projects.pluck(:import_source)
@repos.to_a.reject! { |repo| already_added_projects_names.include? repo.full_name }
end
def jobs
jobs = current_user.created_projects.where(import_type: "gitorious").to_json(only: [:id, :import_status])
render json: jobs
end
def create
@repo_id = params[:repo_id]
repo = client.repo(@repo_id)
@target_namespace = params[:new_namespace].presence || repo.namespace
@project_name = repo.name
namespace = get_or_create_namespace || (render and return)
@project = Gitlab::GitoriousImport::ProjectCreator.new(repo, namespace, current_user).execute
end
private
def client
@client ||= Gitlab::GitoriousImport::Client.new(session[:gitorious_repos])
end
end
......@@ -2,9 +2,9 @@
class Projects::BlameController < Projects::ApplicationController
include ExtractsPath
before_filter :require_non_empty_project
before_filter :assign_ref_vars
before_filter :authorize_download_code!
before_filter :require_non_empty_project
def show
@blob = @repository.blob_at(@commit.id, @path)
......
......@@ -5,8 +5,8 @@ class Projects::BlobController < Projects::ApplicationController
# Raised when given an invalid file path
class InvalidPathError < StandardError; end
before_filter :authorize_download_code!
before_filter :require_non_empty_project, except: [:new, :create]
before_filter :authorize_download_code!
before_filter :authorize_push_code!, only: [:destroy]
before_filter :assign_blob_vars
before_filter :commit, except: [:new, :create]
......
......@@ -2,7 +2,6 @@ class Projects::BranchesController < Projects::ApplicationController
include ActionView::Helpers::SanitizeHelper
# Authorize
before_filter :require_non_empty_project
before_filter :authorize_download_code!
before_filter :authorize_push_code!, only: [:create, :destroy]
......
......@@ -3,8 +3,8 @@
# Not to be confused with CommitsController, plural.
class Projects::CommitController < Projects::ApplicationController
# Authorize
before_filter :authorize_download_code!
before_filter :require_non_empty_project
before_filter :authorize_download_code!
before_filter :commit
def show
......
......@@ -3,9 +3,9 @@ require "base64"
class Projects::CommitsController < Projects::ApplicationController
include ExtractsPath
before_filter :require_non_empty_project
before_filter :assign_ref_vars
before_filter :authorize_download_code!
before_filter :require_non_empty_project
def show
@repo = @project.repository
......
class Projects::CompareController < Projects::ApplicationController
# Authorize
before_filter :authorize_download_code!
before_filter :require_non_empty_project
before_filter :authorize_download_code!
def index
end
......
class Projects::ForksController < Projects::ApplicationController
# Authorize
before_filter :authorize_download_code!
before_filter :require_non_empty_project
before_filter :authorize_download_code!
def new
@namespaces = current_user.manageable_namespaces
......
class Projects::GraphsController < Projects::ApplicationController
# Authorize
before_filter :authorize_download_code!
before_filter :require_non_empty_project
before_filter :authorize_download_code!
def show
respond_to do |format|
......
......@@ -2,9 +2,9 @@ class Projects::NetworkController < Projects::ApplicationController
include ExtractsPath
include ApplicationHelper
before_filter :require_non_empty_project
before_filter :assign_ref_vars
before_filter :authorize_download_code!
before_filter :require_non_empty_project
def show
respond_to do |format|
......
......@@ -2,9 +2,9 @@
class Projects::RawController < Projects::ApplicationController
include ExtractsPath
before_filter :require_non_empty_project
before_filter :assign_ref_vars
before_filter :authorize_download_code!
before_filter :require_non_empty_project
def show
@blob = @repository.blob_at(@commit.id, @path)
......
class Projects::RefsController < Projects::ApplicationController
include ExtractsPath
before_filter :require_non_empty_project
before_filter :assign_ref_vars
before_filter :authorize_download_code!
before_filter :require_non_empty_project
def switch
respond_to do |format|
......
class Projects::RepositoriesController < Projects::ApplicationController
# Authorize
before_filter :authorize_download_code!
before_filter :require_non_empty_project, except: :create
before_filter :authorize_download_code!
before_filter :authorize_admin_project!, only: :create
def create
......
......@@ -2,9 +2,9 @@
class Projects::TreeController < Projects::ApplicationController
include ExtractsPath
before_filter :require_non_empty_project, except: [:new, :create]
before_filter :assign_ref_vars
before_filter :authorize_download_code!
before_filter :require_non_empty_project, except: [:new, :create]
def show
if tree.entries.empty?
......
class Projects::UploadsController < Projects::ApplicationController
layout "project"
before_filter :project
def show
path = File.join(project.path_with_namespace, params[:secret])
uploader = FileUploader.new('uploads', path)
uploader.retrieve_from_store!(params[:filename])
if uploader.file.exists?
# Right now, these are always images, so we can safely render them inline.
send_file uploader.file.path, disposition: 'inline'
else
not_found!
end
end
end
class UploadsController < ApplicationController
def show
model = params[:model].camelize.constantize.find(params[:id])
uploader = model.send(params[:mounted_as])
if uploader.file_storage?
if !model.respond_to?(:project) || can?(current_user, :read_project, model.project)
disposition = uploader.image? ? 'inline' : 'attachment'
send_file uploader.file.path, disposition: disposition
else
not_found!
end
else
redirect_to uploader.url
end
end
end
......@@ -409,19 +409,19 @@ class Note < ActiveRecord::Base
prev_lines = []
diff_lines.each do |line|
if generate_line_code(line) != self.line_code
if line.type == "match"
prev_lines.clear
prev_match_line = line
else
prev_lines.push(line)
prev_lines.shift if prev_lines.length >= max_number_of_lines
end
if line.type == "match"
prev_lines.clear
prev_match_line = line
else
prev_lines << line
return prev_lines
break if generate_line_code(line) == self.line_code
prev_lines.shift if prev_lines.length >= max_number_of_lines
end
end
prev_lines
end
def diff_lines
......
......@@ -16,9 +16,6 @@ module MergeRequests
return build_failed(merge_request, nil)
end
# Generate suggested MR title based on source branch name
merge_request.title = merge_request.source_branch.titleize.humanize
compare_result = CompareService.new.execute(
current_user,
merge_request.source_project,
......@@ -52,6 +49,15 @@ module MergeRequests
merge_request.compare_failed = false
end
commits = merge_request.compare_commits
if commits && commits.count == 1
commit = commits.first
merge_request.title = commit.title
merge_request.description = commit.description.try(:strip)
else
merge_request.title = merge_request.source_branch.titleize.humanize
end
merge_request
rescue Gitlab::Satellite::BranchesWithoutParent
......
%h3.page-title
%i.fa.fa-gitorious
Import repositories from Gitorious.org
%p.light
Select projects you want to import.
%hr
%p
= button_tag 'Import all projects', class: "btn btn-success js-import-all"
%table.table.import-jobs
%thead
%tr
%th From Gitorious
%th To GitLab
%th Status
%tbody
- @already_added_projects.each do |project|
%tr{id: "project_#{project.id}", class: "#{project_status_css_class(project.import_status)}"}
%td= project.import_source
%td
%strong= link_to project.path_with_namespace, project
%td.job-status
- if project.import_status == 'finished'
%span.cgreen
%i.fa.fa-check
done
- else
= project.human_import_status_name
- @repos.each do |repo|
%tr{id: "repo_#{repo.id}"}
%td= repo.full_name
%td.import-target
= repo.full_name
%td.import-actions.job-status
= button_tag "Import", class: "btn js-add-to-import"
:coffeescript
$ ->
new ImporterStatus("#{jobs_import_gitorious_path}", "#{import_gitorious_path}")
%head
%meta{charset: "utf-8"}
-# Go repository retrieval support
-# Need to be the fist thing in the head
-# Since Go is using an XML parser to process HTML5
-# https://github.com/gitlabhq/gitlabhq/pull/5958#issuecomment-45397555
- if controller_name == 'projects' && action_name == 'show'
%meta{name: "go-import", content: "#{@project.web_url_without_protocol} git #{@project.web_url}.git"}
%meta{content: "GitLab Community Edition", name: "description"}
%title
......
%li.commit.js-toggle-container
.commit-row-title
= link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id"
&nbsp;
%span.str-truncated
%strong.str-truncated
= link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-row-message"
- if commit.description?
%a.text-expander.js-toggle-button ...
= link_to_browse_code(project, commit)
.pull-right
= link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id"
.notes_count
- if @note_counts
......@@ -17,8 +16,9 @@
- note_count = notes.count
- if note_count > 0
%span.label.label-gray
%i.fa.fa-comment= note_count
%span.light
%i.fa.fa-comments
= note_count
- if commit.description?
.commit-row-description.js-toggle-content
......@@ -26,6 +26,8 @@
= preserve(gfm(escape_once(commit.description)))
.commit-row-info
= commit_author_link(commit, avatar: true, size: 16)
= commit_author_link(commit, avatar: true, size: 24)
authored
.committed_ago
#{time_ago_with_tooltip(commit.committed_date)} &nbsp;
= link_to_browse_code(project, commit)
......@@ -3,12 +3,13 @@
- @commits.group_by { |c| c.committed_date.to_date }.sort.reverse.each do |day, commits|
.row.commits-row
.col-md-2
%h4
.col-md-2.hidden-xs.hidden-sm
%h5.commits-row-date
%i.fa.fa-calendar
%span= day.stamp("28 Aug, 2010")
%p= pluralize(commits.count, 'commit')
.col-md-10
.light
= pluralize(commits.count, 'commit')
.col-md-10.col-sm-12
%ul.bordered-list
= render commits, project: project
%hr.lists-separator
......@@ -7,8 +7,7 @@
.row
.col-md-9
.participants
%cite.cgray
= pluralize(@issue.participants.count, 'participant')
%span= pluralize(@issue.participants.count, 'participant')
- @issue.participants.each do |participant|
= link_to_member(@project, participant, name: false, size: 24)
......@@ -20,8 +19,7 @@
= cross_project_reference(@project, @issue)
%hr
.context
%cite.cgray
= render partial: 'issue_context', locals: { issue: @issue }
= render partial: 'issue_context', locals: { issue: @issue }
%hr
.clearfix
.votes-holder
......
......@@ -6,9 +6,15 @@
.issue-title
%span.str-truncated
= link_to_gfm issue.title, namespace_project_issue_path(issue.project.namespace, issue.project, issue), class: "row_title"
- if issue.closed?
%small.pull-right
CLOSED
.pull-right.light
- if issue.closed?
%span
CLOSED
- if issue.notes.any?
&nbsp;
%span
%i.fa.fa-comments
= issue.notes.count
.issue-info
%span.light= "##{issue.iid}"
......@@ -16,10 +22,6 @@
assigned to #{link_to_member(@project, issue.assignee)}
- if issue.votes_count > 0
= render 'votes/votes_inline', votable: issue
- if issue.notes.any?
%span
%i.fa.fa-comments
= issue.notes.count
- if issue.milestone
%span
%i.fa.fa-clock-o
......
= form_for [@project.namespace.becomes(Namespace), @project, @issue], remote: true, html: {class: 'edit-issue inline-update'} do |f|
%div.prepend-top-20
%p
Assignee:
.issuable-context-title
%label
Assignee:
- if issue.assignee
= link_to_member(@project, @issue.assignee)
%strong= link_to_member(@project, @issue.assignee, size: 24)
- else
none
- if can?(current_user, :modify_issue, @issue)
= project_users_select_tag('issue[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @issue.assignee_id)
%div.prepend-top-20
%p
Milestone:
%div.prepend-top-20.clearfix
.issuable-context-title
%label
Milestone:
- if issue.milestone
#{link_to @issue.milestone.title, namespace_project_milestone_path(@project.namespace, @project, @issue.milestone)}
%span.back-to-milestone
= link_to namespace_project_milestone_path(@project.namespace, @project, @issue.milestone) do
%strong
%i.fa.fa-clock-o
= @issue.milestone.title
- else
none
- if can?(current_user, :modify_issue, @issue)
......
......@@ -26,7 +26,7 @@
Edit
%hr
%h3.issue-title
%h2.issue-title
= gfm escape_once(@issue.title)
%div
- if @issue.description.present?
......
......@@ -16,8 +16,7 @@
= cross_project_reference(@project, @merge_request)
%hr
.context
%cite.cgray
= render partial: 'projects/merge_requests/show/context', locals: { merge_request: @merge_request }
= render partial: 'projects/merge_requests/show/context', locals: { merge_request: @merge_request }
%hr
.votes-holder
%h6 Votes
......
%li{ class: mr_css_classes(merge_request) }
.merge-request-title
= link_to_gfm truncate(merge_request.title, length: 80), namespace_project_merge_request_path(merge_request.target_project.namespace, merge_request.target_project, merge_request), class: "row_title"
- if merge_request.merged?
%small.pull-right
%i.fa.fa-check
MERGED
- else
%span.pull-right.hidden-xs
- if merge_request.for_fork?
%span.light
#{merge_request.source_project_namespace}:
= truncate merge_request.source_branch, length: 25
%i.fa.fa-angle-right.light
= merge_request.target_branch
%span.str-truncated
= link_to_gfm merge_request.title, namespace_project_merge_request_path(merge_request.target_project.namespace, merge_request.target_project, merge_request), class: "row_title"
.pull-right.light
- if merge_request.merged?
%span
%i.fa.fa-check
MERGED
- elsif merge_request.closed?
%span
%i.fa.fa-close
CLOSED
- else
%span.hidden-xs.hidden-sm
%span.label-branch<
%i.fa.fa-code-fork
%span= merge_request.target_branch
- if merge_request.notes.any?
&nbsp;
%span
%i.fa.fa-comments
= merge_request.mr_and_commit_notes.count
.merge-request-info
%span.light= "##{merge_request.iid}"
- if merge_request.assignee
......@@ -21,10 +29,6 @@
Unassigned
- if merge_request.votes_count > 0
= render 'votes/votes_inline', votable: merge_request
- if merge_request.notes.any?
%span
%i.fa.fa-comments
= merge_request.mr_and_commit_notes.count
- if merge_request.milestone_id?
%span
%i.fa.fa-clock-o
......@@ -33,6 +37,7 @@
%span.task-status
= merge_request.task_status
.pull-right.hidden-xs
%small updated #{time_ago_with_tooltip(merge_request.updated_at, 'bottom', 'merge_request_updated_ago')}
......
= form_for [@project.namespace.becomes(Namespace), @project, @merge_request], remote: true, html: {class: 'edit-merge_request inline-update'} do |f|
%div.prepend-top-20
%p
Assignee:
.issuable-context-title
%label
Assignee:
- if @merge_request.assignee
= link_to_member(@project, @merge_request.assignee)
%strong= link_to_member(@project, @merge_request.assignee, size: 24)
- else
none
- if can?(current_user, :modify_merge_request, @merge_request)
= project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @merge_request.assignee_id)
.issuable-context-selectbox
- if can?(current_user, :modify_merge_request, @merge_request)
= project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @merge_request.assignee_id)
%div.prepend-top-20
%p
Milestone:
%div.prepend-top-20.clearfix
.issuable-context-title
%label
Milestone:
- if @merge_request.milestone
%span.back-to-milestone
#{link_to @merge_request.milestone.title, namespace_project_milestone_path(@project.namespace, @project, @merge_request.milestone)}
= link_to namespace_project_milestone_path(@project.namespace, @project, @merge_request.milestone) do
%strong
%i.fa.fa-clock-o
= @merge_request.milestone.title
- else
none
- if can?(current_user, :modify_merge_request, @merge_request)
= f.select(:milestone_id, milestone_options(@merge_request), { include_blank: "Select milestone" }, {class: 'select2 select2-compact js-select2 js-milestone'})
= hidden_field_tag :merge_request_context
= f.submit class: 'btn'
.issuable-context-selectbox
- if can?(current_user, :modify_merge_request, @merge_request)
= f.select(:milestone_id, milestone_options(@merge_request), { include_blank: "Select milestone" }, {class: 'select2 select2-compact js-select2 js-milestone'})
= hidden_field_tag :merge_request_context
= f.submit class: 'btn'
%h3.issue-title
%h2.issue-title
= gfm escape_once(@merge_request.title)
%div
......
.participants
%cite.cgray #{@merge_request.participants.count} participants
%span #{@merge_request.participants.count} participants
- @merge_request.participants.each do |participant|
= link_to_member(@project, participant, name: false, size: 24)
......@@ -53,18 +53,26 @@
Import projects from GitHub
= render 'github_import_modal'
- unless request.host == 'gitlab.com'
.project-import.form-group
.col-sm-2
.col-sm-10
- if gitlab_import_enabled?
= link_to status_import_gitlab_path do
%i.fa.fa-heart
Import projects from GitLab.com
- else
= link_to '#', class: 'how_to_import_link light' do
%i.fa.fa-heart
Import projects from GitLab.com
= render 'gitlab_import_modal'
.project-import.form-group
.col-sm-2
.col-sm-10
- if gitlab_import_enabled?
= link_to status_import_gitlab_path do
%i.fa.fa-heart
Import projects from GitLab.com
- elsif request.host != 'gitlab.com'
= link_to '#', class: 'how_to_import_link light' do
%i.fa.fa-heart
Import projects from GitLab.com
= render 'gitlab_import_modal'
= link_to new_import_gitorious_path do
%i.fa.fa-heart
Import projects from Gitorious.org
%hr.prepend-botton-10
......
app = Rails.application
if app.config.serve_static_assets
# The `ActionDispatch::Static` middleware intercepts requests for static files
# by checking if they exist in the `/public` directory.
# We're replacing it with our `Gitlab::Middleware::Static` that does the same,
# except ignoring `/uploads`, letting those go through to the GitLab Rails app.
app.config.middleware.swap(
ActionDispatch::Static,
Gitlab::Middleware::Static,
app.paths["public"].first,
app.config.static_cache_control
)
end
......@@ -67,9 +67,29 @@ Gitlab::Application.routes.draw do
get :callback
get :jobs
end
resource :gitorious, only: [:create, :new], controller: :gitorious do
get :status
get :callback
get :jobs
end
end
#
# Uploads
#
scope path: :uploads do
# Note attachments and User/Group/Project avatars
get ":model/:mounted_as/:id/:filename",
to: "uploads#show",
constraints: { model: /note|user|group|project/, mounted_as: /avatar|attachment/, filename: /.+/ }
# Project markdown uploads
get ":id/:secret/:filename",
to: "projects/uploads#show",
constraints: { id: /[a-zA-Z.0-9_\-]+\/[a-zA-Z.0-9_\-]+/, filename: /.+/ }
end
#
# Explore area
......
# Projects
### Project visibility level
Project in GitLab has be either private, internal or public.
You can determine it by `visibility_level` field in project.
Constants for project visibility levels are next:
* Private. `visibility_level` is `0`.
Project access must be granted explicitly for each user.
* Internal. `visibility_level` is `10`.
The project can be cloned by any logged in user.
* Public. `visibility_level` is `20`.
The project can be cloned without any authentication.
## List projects
Get a list of projects accessible by the authenticated user.
......
......@@ -164,8 +164,6 @@ git diff 6-0-stable:config/gitlab.yml.example 7-8-stable:config/gitlab.yml.examp
* Make `/home/git/gitlab/config/gitlab.yml` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-8-stable/config/gitlab.yml.example but with your settings.
* Make `/home/git/gitlab/config/unicorn.rb` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-8-stable/config/unicorn.rb.example but with your settings.
* Make `/home/git/gitlab-shell/config.yml` the same as https://gitlab.com/gitlab-org/gitlab-shell/blob/v2.4.3/config.yml.example but with your settings.
* HTTP setups: Make `/etc/nginx/sites-available/gitlab` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-8-stable/lib/support/nginx/gitlab but with your settings.
* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-8-stablef/lib/support/nginx/gitlab-ssl but with your settings.
* Copy rack attack middleware config
```bash
......@@ -178,6 +176,12 @@ sudo -u git -H cp config/initializers/rack_attack.rb.example config/initializers
sudo cp lib/support/logrotate/gitlab /etc/logrotate.d/gitlab
```
### Change Nginx settings
* HTTP setups: Make `/etc/nginx/sites-available/gitlab` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-8-stable/lib/support/nginx/gitlab but with your settings.
* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-8-stablef/lib/support/nginx/gitlab-ssl but with your settings.
* A new `location /uploads/` section has been added that needs to have the same content as the existing `location @gitlab` section.
## 9. Start application
sudo service gitlab start
......
......@@ -75,8 +75,9 @@ git diff origin/7-6-stable:config/gitlab.yml.example origin/7-8-stable:config/gi
#### Change Nginx settings
* HTTP setups: Make `/etc/nginx/sites-available/gitlab` the same as [`lib/support/nginx/gitlab`](/lib/support/nginx/gitlab) but with your settings
* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as [`lib/support/nginx/gitlab-ssl`](/lib/support/nginx/gitlab-ssl) but with your setting
* HTTP setups: Make `/etc/nginx/sites-available/gitlab` the same as [`lib/support/nginx/gitlab`](/lib/support/nginx/gitlab) but with your settings.
* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as [`lib/support/nginx/gitlab-ssl`](/lib/support/nginx/gitlab-ssl) but with your settings.
* A new `location /uploads/` section has been added that needs to have the same content as the existing `location @gitlab` section.
#### Setup time zone (optional)
......
......@@ -24,16 +24,19 @@ Triggered when you push to the repository except when pushing tags.
"project_id": 15,
"repository": {
"name": "Diaspora",
"url": "git@example.com:diaspora.git",
"url": "git@example.com:mike/diasporadiaspora.git",
"description": "",
"homepage": "http://example.com/diaspora"
"homepage": "http://example.com/mike/diaspora",
"git_http_url":"http://example.com/mike/diaspora.git",
"git_ssh_url":"git@example.com:mike/diaspora.git",
"visibility_level":0
},
"commits": [
{
"id": "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327",
"message": "Update Catalan translation to e38cb41.",
"timestamp": "2011-12-12T14:27:31+02:00",
"url": "http://example.com/diaspora/commits/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327",
"url": "http://example.com/mike/diaspora/commit/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327",
"author": {
"name": "Jordi Mallach",
"email": "jordi@softcatala.org"
......@@ -43,7 +46,7 @@ Triggered when you push to the repository except when pushing tags.
"id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
"message": "fixed readme",
"timestamp": "2012-01-03T23:36:29+02:00",
"url": "http://example.com/diaspora/commits/da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
"url": "http://example.com/mike/diaspora/commit/da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
"author": {
"name": "GitLab dev user",
"email": "gitlabdev@dv6700.(none)"
......@@ -72,8 +75,13 @@ Triggered when you create (or delete) tags to the repository.
"name": "jsmith",
"url": "ssh://git@example.com/jsmith/example.git",
"description": "",
"homepage": "http://example.com/jsmith/example"
}
"homepage": "http://example.com/jsmith/example",
"git_http_url":"http://example.com/jsmith/example.git",
"git_ssh_url":"git@example.com:jsmith/example.git",
"visibility_level":0
},
"commits": [],
"total_commits_count": 0
}
```
......
......@@ -27,7 +27,7 @@ module Gitlab
line_old = line.match(/\-[0-9]*/)[0].to_i.abs rescue 0
line_new = line.match(/\+[0-9]*/)[0].to_i.abs rescue 0
next if line_old == 1 && line_new == 1 #top of file
next if line_old <= 1 && line_new <= 1 #top of file
lines_obj << Gitlab::Diff::Line.new(full_line, type, line_obj_index, line_old, line_new)
line_obj_index += 1
next
......
module Gitlab
module GitoriousImport
GITORIOUS_HOST = "https://gitorious.org"
class Client
attr_reader :repo_list
def initialize(repo_list)
@repo_list = repo_list
end
def authorize_url(redirect_uri)
"#{GITORIOUS_HOST}/gitlab-import?callback_url=#{redirect_uri}"
end
def repos
@repos ||= repo_names.map { |full_name| Repository.new(full_name) }
end
def repo(id)
repos.find { |repo| repo.id == id }
end
private
def repo_names
repo_list.to_s.split(',').map(&:strip).reject(&:blank?)
end
end
Repository = Struct.new(:full_name) do
def id
Digest::SHA1.hexdigest(full_name)
end
def namespace
segments.first
end
def path
segments.last
end
def name
path.titleize
end
def description
""
end
def import_url
"#{GITORIOUS_HOST}/#{full_name}.git"
end
private
def segments
full_name.split('/')
end
end
end
end
module Gitlab
module GitoriousImport
class ProjectCreator
attr_reader :repo, :namespace, :current_user
def initialize(repo, namespace, current_user)
@repo = repo
@namespace = namespace
@current_user = current_user
end
def execute
@project = Project.new(
name: repo.name,
path: repo.path,
description: repo.description,
namespace: namespace,
creator: current_user,
visibility_level: Gitlab::VisibilityLevel::PUBLIC,
import_type: "gitorious",
import_source: repo.full_name,
import_url: repo.import_url
)
if @project.save!
@project.reload
if @project.import_failed?
@project.import_retry
else
@project.import_start
end
end
@project
end
end
end
end
module Gitlab
module Middleware
class Static < ActionDispatch::Static
UPLOADS_REGEX = /\A\/uploads(\/|\z)/.freeze
def call(env)
return @app.call(env) if env['PATH_INFO'] =~ UPLOADS_REGEX
super
end
end
end
end
......@@ -41,6 +41,9 @@ module Gitlab
url: project.url_to_repo,
description: project.description,
homepage: project.web_url,
git_http_url: project.http_url_to_repo,
git_ssh_url: project.ssh_url_to_repo,
visibility_level: project.visibility_level
},
commits: [],
total_commits_count: commits_count
......
## GitLab
## Contributors: randx, yin8086, sashkab, orkoden, axilleas, bbodenmiller
## Contributors: randx, yin8086, sashkab, orkoden, axilleas, bbodenmiller, DouweM
##
## Lines starting with two hashes (##) are comments with information.
## Lines starting with one hash (#) are configuration parameters that can be uncommented.
......@@ -56,6 +56,37 @@ server {
try_files $uri $uri/index.html $uri.html @gitlab;
}
## We route uploads through GitLab to prevent XSS and enforce access control.
location /uploads/ {
## If you use HTTPS make sure you disable gzip compression
## to be safe against BREACH attack.
# gzip off;
## https://github.com/gitlabhq/gitlabhq/issues/694
## Some requests take more than 30 seconds.
proxy_read_timeout 300;
proxy_connect_timeout 300;
proxy_redirect off;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Frame-Options SAMEORIGIN;
proxy_pass http://gitlab;
}
## If ``go get`` detected, return go-import meta tag.
## This works for public and for private repositories.
## See also http://golang.org/cmd/go/#hdr-Remote_import_paths
if ($http_user_agent ~* "Go") {
return 200 "
<!DOCTYPE html>
<head><meta content='$host$uri git $scheme://$host$uri.git' name='go-import'></head>
</html>";
}
## If a file, which is not found in the root folder is requested,
## then the proxy passes the request to the upsteam (gitlab unicorn).
location @gitlab {
......
## GitLab
## Contributors: randx, yin8086, sashkab, orkoden, axilleas, bbodenmiller
## Contributors: randx, yin8086, sashkab, orkoden, axilleas, bbodenmiller, DouweM
##
## Modified from nginx http version
## Modified from http://blog.phusion.nl/2012/04/21/tutorial-setting-up-gitlab-on-debian-6/
......@@ -101,6 +101,38 @@ server {
try_files $uri $uri/index.html $uri.html @gitlab;
}
## We route uploads through GitLab to prevent XSS and enforce access control.
location /uploads/ {
## If you use HTTPS make sure you disable gzip compression
## to be safe against BREACH attack.
gzip off;
## https://github.com/gitlabhq/gitlabhq/issues/694
## Some requests take more than 30 seconds.
proxy_read_timeout 300;
proxy_connect_timeout 300;
proxy_redirect off;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Ssl on;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Frame-Options SAMEORIGIN;
proxy_pass http://gitlab;
}
## If ``go get`` detected, return go-import meta tag.
## This works for public and for private repositories.
## See also http://golang.org/cmd/go/#hdr-Remote_import_paths
if ($http_user_agent ~* "Go") {
return 200 "
<!DOCTYPE html>
<head><meta content='$host$uri git $scheme://$host$uri.git' name='go-import'></head>
</html>";
}
## If a file, which is not found in the root folder is requested,
## then the proxy passes the request to the upsteam (gitlab unicorn).
location @gitlab {
......
require 'spec_helper'
describe Import::GitoriousController do
let(:user) { create(:user) }
before do
sign_in(user)
end
describe "GET new" do
it "redirects to import endpoint on gitorious.org" do
get :new
expect(controller).to redirect_to("https://gitorious.org/gitlab-import?callback_url=http://test.host/import/gitorious/callback")
end
end
describe "GET callback" do
it "stores repo list in session" do
get :callback, repos: 'foo/bar,baz/qux'
expect(session[:gitorious_repos]).to eq('foo/bar,baz/qux')
end
end
describe "GET status" do
before do
@repo = OpenStruct.new(full_name: 'asd/vim')
end
it "assigns variables" do
@project = create(:project, import_type: 'gitorious', creator_id: user.id)
controller.stub_chain(:client, :repos).and_return([@repo])
get :status
expect(assigns(:already_added_projects)).to eq([@project])
expect(assigns(:repos)).to eq([@repo])
end
it "does not show already added project" do
@project = create(:project, import_type: 'gitorious', creator_id: user.id, import_source: 'asd/vim')
controller.stub_chain(:client, :repos).and_return([@repo])
get :status
expect(assigns(:already_added_projects)).to eq([@project])
expect(assigns(:repos)).to eq([])
end
end
describe "POST create" do
before do
@repo = Gitlab::GitoriousImport::Repository.new('asd/vim')
end
it "takes already existing namespace" do
namespace = create(:namespace, name: "asd", owner: user)
expect(Gitlab::GitoriousImport::ProjectCreator).
to receive(:new).with(@repo, namespace, user).
and_return(double(execute: true))
controller.stub_chain(:client, :repo).and_return(@repo)
post :create, format: :js
end
end
end
require 'spec_helper'
describe Gitlab::GitoriousImport::ProjectCreator do
let(:user) { create(:user) }
let(:repo) { Gitlab::GitoriousImport::Repository.new('foo/bar-baz-qux') }
let(:namespace){ create(:namespace) }
it 'creates project' do
allow_any_instance_of(Project).to receive(:add_import_job)
project_creator = Gitlab::GitoriousImport::ProjectCreator.new(repo, namespace, user)
project_creator.execute
project = Project.last
expect(project.name).to eq("Bar Baz Qux")
expect(project.path).to eq("bar-baz-qux")
expect(project.namespace).to eq(namespace)
expect(project.visibility_level).to eq(Gitlab::VisibilityLevel::PUBLIC)
expect(project.import_type).to eq("gitorious")
expect(project.import_source).to eq("foo/bar-baz-qux")
expect(project.import_url).to eq("https://gitorious.org/foo/bar-baz-qux.git")
end
end
......@@ -13,6 +13,9 @@ describe 'Gitlab::PushDataBuilder' do
it { expect(data[:after]).to eq('5937ac0a7beb003549fc5fd26fc247adbce4a52e') }
it { expect(data[:ref]).to eq('refs/heads/master') }
it { expect(data[:commits].size).to eq(3) }
it { expect(data[:repository][:git_http_url]).to eq(project.http_url_to_repo) }
it { expect(data[:repository][:git_ssh_url]).to eq(project.ssh_url_to_repo) }
it { expect(data[:repository][:visibility_level]).to eq(project.visibility_level) }
it { expect(data[:total_commits_count]).to eq(3) }
end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment