Commit b2fb6e0e authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge branch 'master' of dev.gitlab.org:gitlab/gitlabhq

Conflicts:
	app/services/merge_requests/build_service.rb
	app/views/layouts/_head.html.haml
parents 6f0dcde6 626f5bab
v 7.9.0 (unreleased) 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
v 7.8.0 (unreleased) 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) - Replace highlight.js with rouge-fork rugments (Stefan Tatschner)
- Make project search case insensitive (Hannes Rosenögger) - Make project search case insensitive (Hannes Rosenögger)
- Include issue/mr participants in list of recipients for reassign/close/reopen emails - Include issue/mr participants in list of recipients for reassign/close/reopen emails
...@@ -63,6 +65,8 @@ v 7.8.0 (unreleased) ...@@ -63,6 +65,8 @@ v 7.8.0 (unreleased)
- Remove deprecated Group#owner_id from API - Remove deprecated Group#owner_id from API
- Show projects user contributed to on user page. Show stars near project on user page. - Show projects user contributed to on user page. Show stars near project on user page.
- Improve database performance for GitLab - Improve database performance for GitLab
- Add Asana service (Jeremy Benoist)
- Improve project web hooks with extra data
v 7.7.2 v 7.7.2
- Update GitLab Shell to version 2.4.2 that fixes a bug when developers can push to protected branch - Update GitLab Shell to version 2.4.2 that fixes a bug when developers can push to protected branch
......
...@@ -47,6 +47,9 @@ GEM ...@@ -47,6 +47,9 @@ GEM
astrolabe (1.3.0) astrolabe (1.3.0)
parser (>= 2.2.0.pre.3, < 3.0) parser (>= 2.2.0.pre.3, < 3.0)
attr_required (1.0.0) attr_required (1.0.0)
autoprefixer-rails (5.1.6)
execjs
json
awesome_print (1.2.0) awesome_print (1.2.0)
axiom-types (0.0.5) axiom-types (0.0.5)
descendants_tracker (~> 0.0.1) descendants_tracker (~> 0.0.1)
...@@ -57,8 +60,9 @@ GEM ...@@ -57,8 +60,9 @@ GEM
erubis (>= 2.6.6) erubis (>= 2.6.6)
binding_of_caller (0.7.2) binding_of_caller (0.7.2)
debug_inspector (>= 0.0.1) debug_inspector (>= 0.0.1)
bootstrap-sass (3.0.3.0) bootstrap-sass (3.3.3)
sass (~> 3.2) autoprefixer-rails (>= 5.0.0.1)
sass (>= 3.2.19)
browser (0.7.2) browser (0.7.2)
builder (3.2.2) builder (3.2.2)
cal-heatmap-rails (0.0.1) cal-heatmap-rails (0.0.1)
......
...@@ -16,11 +16,8 @@ class @calendar ...@@ -16,11 +16,8 @@ class @calendar
subDomain: "day" subDomain: "day"
range: 12 range: 12
tooltip: true tooltip: true
domainDynamicDimension: false
colLimit: 4
label: label:
position: "top" position: "top"
domainMargin: 1
legend: [ legend: [
0 0
1 1
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
$(this).html totalIssues + 1 $(this).html totalIssues + 1
else else
$(this).html totalIssues - 1 $(this).html totalIssues - 1
$("body").on "click", ".issues-filters .dropdown-menu a", -> $("body").on "click", ".issues-other-filters .dropdown-menu a", ->
$('.issues-list').block( $('.issues-list').block(
message: null, message: null,
overlayCSS: overlayCSS:
...@@ -77,9 +77,9 @@ ...@@ -77,9 +77,9 @@
ids.push $(value).attr("data-id") ids.push $(value).attr("data-id")
$("#update_issues_ids").val ids $("#update_issues_ids").val ids
$(".issues-filters").hide() $(".issues-other-filters").hide()
$(".issues_bulk_update").show() $(".issues_bulk_update").show()
else else
$("#update_issues_ids").val [] $("#update_issues_ids").val []
$(".issues_bulk_update").hide() $(".issues_bulk_update").hide()
$(".issues-filters").show() $(".issues-other-filters").show()
.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{ .commits-compare-switch{
background: image-url("switch_icon.png") no-repeat center center; background: image-url("switch_icon.png") no-repeat center center;
width: 32px; width: 32px;
...@@ -85,60 +11,40 @@ ...@@ -85,60 +11,40 @@
background-color: #EEE; background-color: #EEE;
} }
.commit-description {
background: none;
border: none;
margin: 0;
padding: 0;
margin-top: 10px;
}
.commit-box { .lists-separator {
margin: 10px 0; margin: 10px 0;
border-top: 1px solid #ddd; border-color: #DDD;
border-bottom: 1px solid #ddd; }
padding: 20px 0;
.commit-title { .commits-row {
ul {
margin: 0; margin: 0;
li.commit {
padding: 8px 0;
}
} }
.commit-description { .commits-row-date {
margin-top: 15px; font-size: 15px;
line-height: 20px;
margin-bottom: 5px;
} }
} }
.commits-feed-holder {
float: right;
.commit-stat-summary { .btn {
color: #666; padding: 4px 12px;
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;
} }
} }
.lists-separator {
margin: 10px 0;
border-top: 1px dashed #CCC;
}
/**
* COMMIT ROW
*/
li.commit { li.commit {
.commit-row-title { .commit-row-title {
font-size: $list-font-size; font-size: $list-font-size;
line-height: 20px;
margin-bottom: 2px; margin-bottom: 2px;
.notes_count { .notes_count {
...@@ -156,9 +62,9 @@ li.commit { ...@@ -156,9 +62,9 @@ li.commit {
} }
.commit-row-message { .commit-row-message {
color: #333; color: #444;
&:hover { &:hover {
color: #444;
text-decoration: underline; text-decoration: underline;
} }
} }
...@@ -193,13 +99,14 @@ li.commit { ...@@ -193,13 +99,14 @@ li.commit {
.commit-row-info { .commit-row-info {
color: #777; color: #777;
line-height: 24px;
a { a {
color: #777; color: #777;
} }
.committed_ago { .committed_ago {
float: right; display: inline-block;
} }
} }
...@@ -214,34 +121,3 @@ li.commit { ...@@ -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 @@ ...@@ -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 @@ ...@@ -6,6 +6,7 @@
.issue-title { .issue-title {
margin-bottom: 5px; margin-bottom: 5px;
font-size: $list-font-size; font-size: $list-font-size;
font-weight: bold;
} }
.issue-info { .issue-info {
...@@ -170,9 +171,9 @@ form.edit-issue { ...@@ -170,9 +171,9 @@ form.edit-issue {
} }
} }
h3.issue-title { h2.issue-title {
margin-top: 0; margin-top: 0;
font-size: 2em; font-weight: bold;
} }
.context .select2-container { .context .select2-container {
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
.accept-control { .accept-control {
display: inline-block; display: inline-block;
margin: 0;
margin-left: 20px; margin-left: 20px;
padding: 10px 0; padding: 10px 0;
line-height: 20px; line-height: 20px;
...@@ -31,6 +32,7 @@ ...@@ -31,6 +32,7 @@
.remove_source_checkbox { .remove_source_checkbox {
margin: 0; margin: 0;
font-weight: bold;
} }
} }
} }
...@@ -89,6 +91,7 @@ ...@@ -89,6 +91,7 @@
.merge-request-title { .merge-request-title {
margin-bottom: 5px; margin-bottom: 5px;
font-size: $list-font-size; font-size: $list-font-size;
font-weight: bold;
} }
.merge-request-info { .merge-request-info {
......
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
.sidebar-wrapper { .sidebar-wrapper {
z-index: 99; z-index: 99;
overflow-y: auto;
background: #F5F5F5; background: #F5F5F5;
} }
...@@ -40,12 +39,16 @@ ...@@ -40,12 +39,16 @@
.nav-sidebar li { .nav-sidebar li {
&.active a { &.active a {
color: #111; color: #333;
background: #EEE; background: #FFF !important;
font-weight: bold; font-weight: bold;
border: 1px solid #EEE;
border-right: 1px solid transparent;
border-left: 3px solid $style_color;
&.no-highlight { &.no-highlight {
background: none; background: none;
border: none;
} }
i { i {
...@@ -65,7 +68,7 @@ ...@@ -65,7 +68,7 @@
color: #555; color: #555;
display: block; display: block;
text-decoration: none; text-decoration: none;
padding: 6px 15px; padding: 8px 15px;
font-size: 13px; font-size: 13px;
line-height: 20px; line-height: 20px;
text-shadow: 0 1px 2px #FFF; text-shadow: 0 1px 2px #FFF;
...@@ -74,7 +77,7 @@ ...@@ -74,7 +77,7 @@
&:hover { &:hover {
text-decoration: none; text-decoration: none;
color: #333; color: #333;
background: #DDD; background: #EEE;
} }
&:active, &:focus { &:active, &:focus {
...@@ -122,7 +125,6 @@ ...@@ -122,7 +125,6 @@
.sidebar-wrapper { .sidebar-wrapper {
width: 52px; width: 52px;
overflow-x: hidden;
.nav-sidebar { .nav-sidebar {
margin-top: 20px; margin-top: 20px;
...@@ -133,9 +135,10 @@ ...@@ -133,9 +135,10 @@
li a { li a {
padding-left: 18px; padding-left: 18px;
font-size: 14px; font-size: 14px;
padding: 10px 15px; padding: 8px 15px;
text-align: center; text-align: center;
& > span { & > span {
display: none; display: none;
} }
......
...@@ -169,7 +169,7 @@ ...@@ -169,7 +169,7 @@
color: #999; color: #999;
background: #FFF; background: #FFF;
padding: 5px; padding: 5px;
margin-top: -7px; margin-top: -11px;
border: 1px solid #DDD; border: 1px solid #DDD;
font-size: 13px; 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
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
...@@ -55,14 +55,13 @@ class User < ActiveRecord::Base ...@@ -55,14 +55,13 @@ class User < ActiveRecord::Base
include Gitlab::ConfigHelper include Gitlab::ConfigHelper
include TokenAuthenticatable include TokenAuthenticatable
extend Gitlab::ConfigHelper extend Gitlab::ConfigHelper
extend Gitlab::CurrentSettings include Gitlab::CurrentSettings
default_value_for :admin, false default_value_for :admin, false
default_value_for :can_create_group, gitlab_config.default_can_create_group default_value_for :can_create_group, gitlab_config.default_can_create_group
default_value_for :can_create_team, false default_value_for :can_create_team, false
default_value_for :hide_no_ssh_key, false default_value_for :hide_no_ssh_key, false
default_value_for :hide_no_password, false default_value_for :hide_no_password, false
default_value_for :projects_limit, current_application_settings.default_projects_limit
default_value_for :theme_id, gitlab_config.default_theme default_value_for :theme_id, gitlab_config.default_theme
devise :database_authenticatable, :lockable, :async, devise :database_authenticatable, :lockable, :async,
...@@ -141,6 +140,7 @@ class User < ActiveRecord::Base ...@@ -141,6 +140,7 @@ class User < ActiveRecord::Base
before_save :ensure_authentication_token before_save :ensure_authentication_token
after_save :ensure_namespace_correct after_save :ensure_namespace_correct
after_initialize :set_projects_limit
after_create :post_create_hook after_create :post_create_hook
after_destroy :post_destroy_hook after_destroy :post_destroy_hook
...@@ -475,6 +475,13 @@ class User < ActiveRecord::Base ...@@ -475,6 +475,13 @@ class User < ActiveRecord::Base
end end
end end
def set_projects_limit
connection_default_value_defined = new_record? && !projects_limit_changed?
return unless self.projects_limit.nil? || connection_default_value_defined
self.projects_limit = current_application_settings.default_projects_limit
end
def requires_ldap_check? def requires_ldap_check?
if !Gitlab.config.ldap.enabled if !Gitlab.config.ldap.enabled
false false
......
...@@ -16,12 +16,6 @@ module MergeRequests ...@@ -16,12 +16,6 @@ module MergeRequests
return build_failed(merge_request, nil) return build_failed(merge_request, nil)
end end
# Generate suggested MR title based on source branch name
merge_request.title = merge_request.source_branch.titleize.humanize
# Set MR description based on project template
merge_request.description = merge_request.target_project.merge_requests_template
compare_result = CompareService.new.execute( compare_result = CompareService.new.execute(
current_user, current_user,
merge_request.source_project, merge_request.source_project,
...@@ -55,6 +49,20 @@ module MergeRequests ...@@ -55,6 +49,20 @@ module MergeRequests
merge_request.compare_failed = false merge_request.compare_failed = false
end 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
# Set MR description based on project template
if merge_request.target_project.merge_requests_template.present?
merge_request.description = merge_request.target_project.merge_requests_template
end
merge_request merge_request
rescue Gitlab::Satellite::BranchesWithoutParent rescue Gitlab::Satellite::BranchesWithoutParent
......
...@@ -13,15 +13,13 @@ ...@@ -13,15 +13,13 @@
.form-group .form-group
%strong Activity %strong Activity
.checkbox .checkbox
= label_tag :with_push, 'Not empty' = label_tag :with_push do
= check_box_tag :with_push, 1, params[:with_push] = check_box_tag :with_push, 1, params[:with_push]
&nbsp; %span Projects with push events
%span.light Projects with push events
.checkbox .checkbox
= label_tag :abandoned, 'Abandoned' = label_tag :abandoned do
= check_box_tag :abandoned, 1, params[:abandoned] = check_box_tag :abandoned, 1, params[:abandoned]
&nbsp; %span No activity over 6 month
%span.light No activity over 6 month
%fieldset %fieldset
%strong Visibility level: %strong Visibility level:
......
...@@ -2,11 +2,11 @@ ...@@ -2,11 +2,11 @@
= f.text_field :login, class: "form-control top", placeholder: "Username or Email", autofocus: "autofocus" = f.text_field :login, class: "form-control top", placeholder: "Username or Email", autofocus: "autofocus"
= f.password_field :password, class: "form-control bottom", placeholder: "Password" = f.password_field :password, class: "form-control bottom", placeholder: "Password"
- if devise_mapping.rememberable? - if devise_mapping.rememberable?
.remember-me .remember-me.checkbox
%label.checkbox.remember_me{for: "user_remember_me"} %label{for: "user_remember_me"}
= f.check_box :remember_me = f.check_box :remember_me
%span Remember me %span Remember me
.pull-right .pull-right
= link_to "Forgot your password?", new_password_path(resource_name) = link_to "Forgot your password?", new_password_path(resource_name)
%div %div
= f.submit "Sign in", class: "btn btn-save" = f.submit "Sign in", class: "btn btn-save"
%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 %head
%meta{charset: "utf-8"} %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 Enterprise Edition", name: "description"} %meta{content: "GitLab Enterprise Edition", name: "description"}
%title %title
......
...@@ -49,8 +49,14 @@ ...@@ -49,8 +49,14 @@
%span %span
Graphs Graphs
= nav_link(controller: :milestones) do
= link_to project_milestones_path(@project), title: 'Milestones' do
%i.fa.fa-clock-o
%span
Milestones
- if project_nav_tab? :issues - if project_nav_tab? :issues
= nav_link(controller: %w(issues milestones labels)) do = nav_link(controller: :issues) do
= link_to url_for_project_issues, title: 'Issues', class: 'shortcuts-issues' do = link_to url_for_project_issues, title: 'Issues', class: 'shortcuts-issues' do
%i.fa.fa-exclamation-circle %i.fa.fa-exclamation-circle
%span %span
...@@ -66,6 +72,12 @@ ...@@ -66,6 +72,12 @@
Merge Requests Merge Requests
%span.count.merge_counter= @project.merge_requests.opened.count %span.count.merge_counter= @project.merge_requests.opened.count
= nav_link(controller: :labels) do
= link_to project_labels_path(@project), title: 'Labels' do
%i.fa.fa-tags
%span
Labels
- if project_nav_tab? :wiki - if project_nav_tab? :wiki
= nav_link(controller: :wikis) do = nav_link(controller: :wikis) do
= link_to project_wiki_path(@project, :home), title: 'Wiki', class: 'shortcuts-wiki' do = link_to project_wiki_path(@project, :home), title: 'Wiki', class: 'shortcuts-wiki' do
......
%ul.nav.nav-tabs
- if project_nav_tab? :issues
= nav_link(controller: :issues) do
= link_to project_issues_path(@project), class: "tab" do
%i.fa.fa-exclamation-circle
Issues
- if project_nav_tab? :merge_requests
= nav_link(controller: :merge_requests) do
= link_to project_merge_requests_path(@project), class: "tab" do
%i.fa.fa-tasks
Merge Requests
= nav_link(controller: :milestones) do
= link_to project_milestones_path(@project), class: "tab" do
%i.fa.fa-clock-o
Milestones
= nav_link(controller: :labels) do
= link_to project_labels_path(@project), class: "tab" do
%i.fa.fa-tags
Labels
- if current_controller?(:issues)
- if current_user
%li.hidden-xs
= link_to project_issues_path(@project, :atom, { private_token: current_user.private_token }) do
%i.fa.fa-rss
%li.pull-right
.pull-right
.pull-left
= form_tag project_issues_path(@project), method: :get, id: "issue_search_form", class: 'pull-left issue-search-form' do
.append-right-10.hidden-xs.hidden-sm
= search_field_tag :issue_search, params[:issue_search], { placeholder: 'Filter by title or description', class: 'form-control issue_search search-text-input input-mn-300' }
= hidden_field_tag :state, params['state']
= hidden_field_tag :scope, params['scope']
= hidden_field_tag :assignee_id, params['assignee_id']
= hidden_field_tag :milestone_id, params['milestone_id']
= hidden_field_tag :label_id, params['label_id']
- if can? current_user, :write_issue, @project
= link_to new_project_issue_path(@project, issue: { assignee_id: params[:assignee_id], milestone_id: params[:milestone_id]}), class: "btn btn-new pull-left", title: "New Issue", id: "new_issue_link" do
%i.fa.fa-plus
New Issue
- if current_controller?(:merge_requests)
%li.pull-right
.pull-right
- if can? current_user, :write_merge_request, @project
= link_to new_project_merge_request_path(@project), class: "btn btn-new pull-left", title: "New Merge Request" do
%i.fa.fa-plus
New Merge Request
...@@ -7,8 +7,8 @@ ...@@ -7,8 +7,8 @@
- Gitlab::VisibilityLevel.values.each do |level| - Gitlab::VisibilityLevel.values.each do |level|
.radio .radio
- restricted = restricted_visibility_levels.include?(level) - restricted = restricted_visibility_levels.include?(level)
= f.radio_button :visibility_level, level, checked: (visibility_level == level), disabled: restricted
= label :project_visibility_level, level do = label :project_visibility_level, level do
= f.radio_button :visibility_level, level, checked: (visibility_level == level), disabled: restricted
= visibility_level_icon(level) = visibility_level_icon(level)
.option-title .option-title
= visibility_level_label(level) = visibility_level_label(level)
......
%li.commit.js-toggle-container %li.commit.js-toggle-container
.commit-row-title .commit-row-title
= link_to commit.short_id, project_commit_path(project, commit), class: "commit_short_id" %strong.str-truncated
&nbsp;
%span.str-truncated
= link_to_gfm commit.title, project_commit_path(project, commit.id), class: "commit-row-message" = link_to_gfm commit.title, project_commit_path(project, commit.id), class: "commit-row-message"
- if commit.description? - if commit.description?
%a.text-expander.js-toggle-button ... %a.text-expander.js-toggle-button ...
= link_to_browse_code(project, commit) .pull-right
= link_to commit.short_id, project_commit_path(project, commit), class: "commit_short_id"
.notes_count .notes_count
- if @note_counts - if @note_counts
...@@ -17,8 +16,9 @@ ...@@ -17,8 +16,9 @@
- note_count = notes.count - note_count = notes.count
- if note_count > 0 - if note_count > 0
%span.label.label-gray %span.light
%i.fa.fa-comment= note_count %i.fa.fa-comments
= note_count
- if commit.description? - if commit.description?
.commit-row-description.js-toggle-content .commit-row-description.js-toggle-content
...@@ -26,6 +26,8 @@ ...@@ -26,6 +26,8 @@
= preserve(gfm(escape_once(commit.description))) = preserve(gfm(escape_once(commit.description)))
.commit-row-info .commit-row-info
= commit_author_link(commit, avatar: true, size: 16) = commit_author_link(commit, avatar: true, size: 24)
authored
.committed_ago .committed_ago
#{time_ago_with_tooltip(commit.committed_date)} &nbsp; #{time_ago_with_tooltip(commit.committed_date)} &nbsp;
= link_to_browse_code(project, commit)
...@@ -3,12 +3,13 @@ ...@@ -3,12 +3,13 @@
- @commits.group_by { |c| c.committed_date.to_date }.sort.reverse.each do |day, commits| - @commits.group_by { |c| c.committed_date.to_date }.sort.reverse.each do |day, commits|
.row.commits-row .row.commits-row
.col-md-2 .col-md-2.hidden-xs.hidden-sm
%h4 %h5.commits-row-date
%i.fa.fa-calendar %i.fa.fa-calendar
%span= day.stamp("28 Aug, 2010") %span= day.stamp("28 Aug, 2010")
%p= pluralize(commits.count, 'commit') .light
.col-md-10 = pluralize(commits.count, 'commit')
.col-md-10.col-sm-12
%ul.bordered-list %ul.bordered-list
= render commits, project: project = render commits, project: project
%hr.lists-separator %hr.lists-separator
...@@ -7,8 +7,7 @@ ...@@ -7,8 +7,7 @@
.row .row
.col-md-9 .col-md-9
.participants .participants
%cite.cgray %span= pluralize(@issue.participants.count, 'participant')
= pluralize(@issue.participants.count, 'participant')
- @issue.participants.each do |participant| - @issue.participants.each do |participant|
= link_to_member(@project, participant, name: false, size: 24) = link_to_member(@project, participant, name: false, size: 24)
...@@ -20,8 +19,7 @@ ...@@ -20,8 +19,7 @@
= cross_project_reference(@project, @issue) = cross_project_reference(@project, @issue)
%hr %hr
.context .context
%cite.cgray = render partial: 'issue_context', locals: { issue: @issue }
= render partial: 'issue_context', locals: { issue: @issue }
%hr %hr
.clearfix .clearfix
.votes-holder .votes-holder
......
...@@ -6,9 +6,15 @@ ...@@ -6,9 +6,15 @@
.issue-title .issue-title
%span.str-truncated %span.str-truncated
= link_to_gfm issue.title, project_issue_path(issue.project, issue), class: "row_title" = link_to_gfm issue.title, project_issue_path(issue.project, issue), class: "row_title"
- if issue.closed? .pull-right.light
%small.pull-right - if issue.closed?
CLOSED %span
CLOSED
- if issue.notes.any?
&nbsp;
%span
%i.fa.fa-comments
= issue.notes.count
.issue-info .issue-info
%span.light= "##{issue.iid}" %span.light= "##{issue.iid}"
...@@ -16,10 +22,6 @@ ...@@ -16,10 +22,6 @@
assigned to #{link_to_member(@project, issue.assignee)} assigned to #{link_to_member(@project, issue.assignee)}
- if issue.votes_count > 0 - if issue.votes_count > 0
= render 'votes/votes_inline', votable: issue = render 'votes/votes_inline', votable: issue
- if issue.notes.any?
%span
%i.fa.fa-comments
= issue.notes.count
- if issue.milestone - if issue.milestone
%span %span
%i.fa.fa-clock-o %i.fa.fa-clock-o
......
= form_for [@project, @issue], remote: true, html: {class: 'edit-issue inline-update'} do |f| = form_for [@project, @issue], remote: true, html: {class: 'edit-issue inline-update'} do |f|
%div.prepend-top-20 %div.prepend-top-20
%p .issuable-context-title
Assignee: %label
Assignee:
- if issue.assignee - if issue.assignee
= link_to_member(@project, @issue.assignee) %strong= link_to_member(@project, @issue.assignee, size: 24)
- else - else
none none
- if can?(current_user, :modify_issue, @issue) - 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) = 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 %div.prepend-top-20.clearfix
%p .issuable-context-title
Milestone: %label
Milestone:
- if issue.milestone - if issue.milestone
#{link_to @issue.milestone.title, project_milestone_path(@project, @issue.milestone)} %span.back-to-milestone
= link_to project_milestone_path(@project, @issue.milestone) do
%strong
%i.fa.fa-clock-o
= @issue.milestone.title
- else - else
none none
- if can?(current_user, :modify_issue, @issue) - if can?(current_user, :modify_issue, @issue)
......
.append-bottom-10
.check-all-holder
= check_box_tag "check_all_issues", nil, false, class: "check_all_issues left", disabled: !can?(current_user, :modify_issue, @project)
= render 'shared/issuable_filter'
.clearfix
.issues_bulk_update.hide
= form_tag bulk_update_project_issues_path(@project), method: :post do
= select_tag('update[status]', options_for_select([['Open', 'open'], ['Closed', 'closed']]), prompt: "Status")
= project_users_select_tag('update[assignee_id]', placeholder: 'Assignee')
= select_tag('update[milestone_id]', bulk_update_milestone_options, prompt: "Milestone")
= hidden_field_tag 'update[issues_ids]', []
= hidden_field_tag :status, params[:status]
= button_tag "Update issues", class: "btn update_selected_issues btn-save"
.panel.panel-default .panel.panel-default
%ul.well-list.issues-list %ul.well-list.issues-list
= render @issues = render @issues
......
= render "projects/issues_nav" .append-bottom-10
.pull-right
.pull-left
- if current_user
.hidden-xs.pull-left
= link_to project_issues_path(@project, :atom, { private_token: current_user.private_token }), class: 'btn append-right-10' do
%i.fa.fa-rss
= form_tag project_issues_path(@project), method: :get, id: "issue_search_form", class: 'pull-left issue-search-form' do
.append-right-10.hidden-xs.hidden-sm
= search_field_tag :issue_search, params[:issue_search], { placeholder: 'Filter by title or description', class: 'form-control issue_search search-text-input input-mn-300' }
= hidden_field_tag :state, params['state']
= hidden_field_tag :scope, params['scope']
= hidden_field_tag :assignee_id, params['assignee_id']
= hidden_field_tag :milestone_id, params['milestone_id']
= hidden_field_tag :label_id, params['label_id']
- if can? current_user, :write_issue, @project
= link_to new_project_issue_path(@project, issue: { assignee_id: params[:assignee_id], milestone_id: params[:milestone_id]}), class: "btn btn-new pull-left", title: "New Issue", id: "new_issue_link" do
%i.fa.fa-plus
New Issue
= render 'shared/issuable_filter'
.clearfix
.issues_bulk_update.hide
= form_tag bulk_update_project_issues_path(@project), method: :post do
= select_tag('update[status]', options_for_select([['Open', 'open'], ['Closed', 'closed']]), prompt: "Status")
= project_users_select_tag('update[assignee_id]', placeholder: 'Assignee')
= select_tag('update[milestone_id]', bulk_update_milestone_options, prompt: "Milestone")
= hidden_field_tag 'update[issues_ids]', []
= hidden_field_tag :status, params[:status]
= button_tag "Update issues", class: "btn update_selected_issues btn-save"
.issues-holder .issues-holder
= render "issues" = render "issues"
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
Edit Edit
%hr %hr
%h3.issue-title %h2.issue-title
= gfm escape_once(@issue.title) = gfm escape_once(@issue.title)
%div %div
- if @issue.description.present? - if @issue.description.present?
......
= render "projects/issues_nav"
- if can? current_user, :admin_label, @project - if can? current_user, :admin_label, @project
= link_to new_project_label_path(@project), class: "pull-right btn btn-new" do = link_to new_project_label_path(@project), class: "pull-right btn btn-new" do
New label New label
......
...@@ -16,8 +16,7 @@ ...@@ -16,8 +16,7 @@
= cross_project_reference(@project, @merge_request) = cross_project_reference(@project, @merge_request)
%hr %hr
.context .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 %hr
.votes-holder .votes-holder
%h6 Votes %h6 Votes
......
%li{ class: mr_css_classes(merge_request) } %li{ class: mr_css_classes(merge_request) }
.merge-request-title .merge-request-title
= link_to_gfm truncate(merge_request.title, length: 80), project_merge_request_path(merge_request.target_project, merge_request), class: "row_title" %span.str-truncated
- if merge_request.merged? = link_to_gfm merge_request.title, project_merge_request_path(merge_request.target_project, merge_request), class: "row_title"
%small.pull-right .pull-right.light
%i.fa.fa-check - if merge_request.merged?
MERGED %span
- else %i.fa.fa-check
%span.pull-right.hidden-xs MERGED
- if merge_request.for_fork? - elsif merge_request.closed?
%span.light %span
#{merge_request.source_project_namespace}: %i.fa.fa-close
= truncate merge_request.source_branch, length: 25 CLOSED
%i.fa.fa-angle-right.light - else
= merge_request.target_branch %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 .merge-request-info
%span.light= "##{merge_request.iid}" %span.light= "##{merge_request.iid}"
- if merge_request.assignee - if merge_request.assignee
...@@ -21,10 +29,6 @@ ...@@ -21,10 +29,6 @@
Unassigned Unassigned
- if merge_request.votes_count > 0 - if merge_request.votes_count > 0
= render 'votes/votes_inline', votable: merge_request = 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? - if merge_request.milestone_id?
%span %span
%i.fa.fa-clock-o %i.fa.fa-clock-o
...@@ -33,6 +37,7 @@ ...@@ -33,6 +37,7 @@
%span.task-status %span.task-status
= merge_request.task_status = merge_request.task_status
.pull-right.hidden-xs .pull-right.hidden-xs
%small updated #{time_ago_with_tooltip(merge_request.updated_at, 'bottom', 'merge_request_updated_ago')} %small updated #{time_ago_with_tooltip(merge_request.updated_at, 'bottom', 'merge_request_updated_ago')}
......
= render "projects/issues_nav"
.merge-requests-holder .merge-requests-holder
.append-bottom-10 .append-bottom-10
.pull-right
- if can? current_user, :write_merge_request, @project
= link_to new_project_merge_request_path(@project), class: "btn btn-new pull-left", title: "New Merge Request" do
%i.fa.fa-plus
New Merge Request
= render 'shared/issuable_filter' = render 'shared/issuable_filter'
.panel.panel-default .panel.panel-default
%ul.well-list.mr-list %ul.well-list.mr-list
......
= form_for [@project, @merge_request], remote: true, html: {class: 'edit-merge_request inline-update'} do |f| = form_for [@project, @merge_request], remote: true, html: {class: 'edit-merge_request inline-update'} do |f|
%div.prepend-top-20 %div.prepend-top-20
%p .issuable-context-title
Assignee: %label
Assignee:
- if @merge_request.assignee - if @merge_request.assignee
= link_to_member(@project, @merge_request.assignee) %strong= link_to_member(@project, @merge_request.assignee, size: 24)
- else - else
none none
- if can?(current_user, :modify_merge_request, @merge_request) .issuable-context-selectbox
= project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @merge_request.assignee_id) - 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 %div.prepend-top-20.clearfix
%p .issuable-context-title
Milestone: %label
Milestone:
- if @merge_request.milestone - if @merge_request.milestone
%span.back-to-milestone %span.back-to-milestone
#{link_to @merge_request.milestone.title, project_milestone_path(@project, @merge_request.milestone)} = link_to project_milestone_path(@project, @merge_request.milestone) do
%strong
%i.fa.fa-clock-o
= @merge_request.milestone.title
- else - else
none none
- if can?(current_user, :modify_merge_request, @merge_request) .issuable-context-selectbox
= f.select(:milestone_id, milestone_options(@merge_request), { include_blank: "Select milestone" }, {class: 'select2 select2-compact js-select2 js-milestone'}) - if can?(current_user, :modify_merge_request, @merge_request)
= hidden_field_tag :merge_request_context = f.select(:milestone_id, milestone_options(@merge_request), { include_blank: "Select milestone" }, {class: 'select2 select2-compact js-select2 js-milestone'})
= f.submit class: 'btn' = hidden_field_tag :merge_request_context
= f.submit class: 'btn'
...@@ -17,7 +17,7 @@ ...@@ -17,7 +17,7 @@
.accept-action .accept-action
= f.submit "Accept Merge Request", class: "btn btn-create accept_merge_request" = f.submit "Accept Merge Request", class: "btn btn-create accept_merge_request"
- if can_remove_branch?(@merge_request.source_project, @merge_request.source_branch) && !@merge_request.for_fork? - if can_remove_branch?(@merge_request.source_project, @merge_request.source_branch) && !@merge_request.for_fork?
.accept-control .accept-control.checkbox
= label_tag :should_remove_source_branch, class: "remove_source_checkbox" do = label_tag :should_remove_source_branch, class: "remove_source_checkbox" do
= check_box_tag :should_remove_source_branch = check_box_tag :should_remove_source_branch
Remove source-branch Remove source-branch
......
%h3.issue-title %h2.issue-title
= gfm escape_once(@merge_request.title) = gfm escape_once(@merge_request.title)
%div %div
......
.participants .participants
%cite.cgray #{@merge_request.participants.count} participants %span #{@merge_request.participants.count} participants
- @merge_request.participants.each do |participant| - @merge_request.participants.each do |participant|
= link_to_member(@project, participant, name: false, size: 24) = link_to_member(@project, participant, name: false, size: 24)
= render "projects/issues_nav" .pull-right
.milestones_content - if can? current_user, :admin_milestone, @project
%h3.page-title = link_to new_project_milestone_path(@project), class: "pull-right btn btn-new", title: "New Milestone" do
Milestones %i.fa.fa-plus
- if can? current_user, :admin_milestone, @project New Milestone
= link_to new_project_milestone_path(@project), class: "pull-right btn btn-new", title: "New Milestone" do
%i.fa.fa-plus
New Milestone
= render 'shared/milestones_filter' = render 'shared/milestones_filter'
.milestones .milestones
......
= render "projects/issues_nav"
%h4.page-title %h4.page-title
.issue-box{ class: issue_box_class(@milestone) } .issue-box{ class: issue_box_class(@milestone) }
- if @milestone.closed? - if @milestone.closed?
......
...@@ -66,6 +66,13 @@ ...@@ -66,6 +66,13 @@
Import projects from GitLab.com Import projects from GitLab.com
= render 'gitlab_import_modal' = render 'gitlab_import_modal'
.project-import.form-group
.col-sm-2
.col-sm-10
= link_to new_import_gitorious_path do
%i.fa.fa-heart
Import projects from Gitorious.org
%hr.prepend-botton-10 %hr.prepend-botton-10
.form-group .form-group
......
.issues-filters .issues-filters
.pull-left.append-right-20 .issues-state-filters
%ul.nav.nav-pills.nav-compact %ul.nav.nav-tabs
%li{class: ("active" if params[:state] == 'opened')} %li{class: ("active" if params[:state] == 'opened')}
= link_to page_filter_path(state: 'opened') do = link_to page_filter_path(state: 'opened') do
%i.fa.fa-exclamation-circle %i.fa.fa-exclamation-circle
...@@ -14,99 +14,106 @@ ...@@ -14,99 +14,106 @@
%i.fa.fa-compass %i.fa.fa-compass
All All
.dropdown.inline.assignee-filter %div
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} - if controller.controller_name == 'issues'
%i.fa.fa-user .check-all-holder
%span.light assignee: = check_box_tag "check_all_issues", nil, false,
- if @assignee.present? class: "check_all_issues left",
%strong= @assignee.name disabled: !can?(current_user, :modify_issue, @project)
- elsif params[:assignee_id] == "0" .issues-other-filters
Unassigned .dropdown.inline.assignee-filter
- else %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
Any %i.fa.fa-user
%b.caret %span.light assignee:
%ul.dropdown-menu - if @assignee.present?
%li %strong= @assignee.name
= link_to page_filter_path(assignee_id: nil) do - elsif params[:assignee_id] == "0"
Any Unassigned
= link_to page_filter_path(assignee_id: 0) do - else
Unassigned Any
- @assignees.sort_by(&:name).each do |user| %b.caret
%li %ul.dropdown-menu
= link_to page_filter_path(assignee_id: user.id) do %li
= image_tag avatar_icon(user.email), class: "avatar s16", alt: '' = link_to page_filter_path(assignee_id: nil) do
= user.name Any
= link_to page_filter_path(assignee_id: 0) do
.dropdown.inline.prepend-left-10.author-filter Unassigned
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} - @assignees.sort_by(&:name).each do |user|
%i.fa.fa-user %li
%span.light author: = link_to page_filter_path(assignee_id: user.id) do
- if @author.present? = image_tag avatar_icon(user.email), class: "avatar s16", alt: ''
%strong= @author.name = user.name
- elsif params[:author_id] == "0"
Unassigned
- else
Any
%b.caret
%ul.dropdown-menu
%li
= link_to page_filter_path(author_id: nil) do
Any
= link_to page_filter_path(author_id: 0) do
Unassigned
- @authors.sort_by(&:name).each do |user|
%li
= link_to page_filter_path(author_id: user.id) do
= image_tag avatar_icon(user.email), class: "avatar s16", alt: ''
= user.name
.dropdown.inline.prepend-left-10.milestone-filter
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%i.fa.fa-clock-o
%span.light milestone:
- if @milestone.present?
%strong= @milestone.title
- elsif params[:milestone_id] == "0"
None (backlog)
- else
Any
%b.caret
%ul.dropdown-menu
%li
= link_to page_filter_path(milestone_id: nil) do
Any
= link_to page_filter_path(milestone_id: 0) do
None (backlog)
- @milestones.each do |milestone|
%li
= link_to page_filter_path(milestone_id: milestone.id) do
%strong= milestone.title
%small.light= milestone.expires_at
- if @project .dropdown.inline.prepend-left-10.author-filter
.dropdown.inline.prepend-left-10.labels-filter %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} %i.fa.fa-user
%i.fa.fa-tags %span.light author:
%span.light label: - if @author.present?
- if params[:label_name].present? %strong= @author.name
%strong= params[:label_name] - elsif params[:author_id] == "0"
- else Unassigned
Any - else
%b.caret
%ul.dropdown-menu
%li
= link_to page_filter_path(label_name: nil) do
Any Any
- if @project.labels.any? %b.caret
- @project.labels.each do |label| %ul.dropdown-menu
%li
= link_to page_filter_path(author_id: nil) do
Any
= link_to page_filter_path(author_id: 0) do
Unassigned
- @authors.sort_by(&:name).each do |user|
%li %li
= link_to page_filter_path(label_name: label.name) do = link_to page_filter_path(author_id: user.id) do
= render_colored_label(label) = image_tag avatar_icon(user.email), class: "avatar s16", alt: ''
- else = user.name
.dropdown.inline.prepend-left-10.milestone-filter
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%i.fa.fa-clock-o
%span.light milestone:
- if @milestone.present?
%strong= @milestone.title
- elsif params[:milestone_id] == "0"
None (backlog)
- else
Any
%b.caret
%ul.dropdown-menu
%li %li
= link_to generate_project_labels_path(@project, redirect: request.original_url), method: :post do = link_to page_filter_path(milestone_id: nil) do
%i.fa.fa-plus-circle Any
Create default labels = link_to page_filter_path(milestone_id: 0) do
None (backlog)
- @milestones.each do |milestone|
%li
= link_to page_filter_path(milestone_id: milestone.id) do
%strong= milestone.title
%small.light= milestone.expires_at
- if @project
.dropdown.inline.prepend-left-10.labels-filter
%a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
%i.fa.fa-tags
%span.light label:
- if params[:label_name].present?
%strong= params[:label_name]
- else
Any
%b.caret
%ul.dropdown-menu
%li
= link_to page_filter_path(label_name: nil) do
Any
- if @project.labels.any?
- @project.labels.each do |label|
%li
= link_to page_filter_path(label_name: label.name) do
= render_colored_label(label)
- else
%li
= link_to generate_project_labels_path(@project, redirect: request.original_url), method: :post do
%i.fa.fa-plus-circle
Create default labels
.pull-right .pull-right
= render 'shared/sort_dropdown' = render 'shared/sort_dropdown'
.milestones-filters.append-bottom-10 .milestones-filters.append-bottom-10
%ul.nav.nav-pills.nav-compact %ul.nav.nav-tabs
%li{class: ("active" if params[:state].blank? || params[:state] == 'opened')} %li{class: ("active" if params[:state].blank? || params[:state] == 'opened')}
= link_to milestones_filter_path(state: 'opened') do = link_to milestones_filter_path(state: 'opened') do
%i.fa.fa-exclamation-circle %i.fa.fa-exclamation-circle
......
begin
app = Rails.application
# 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
)
rescue
# If ActionDispatch::Static wasn't loaded onto the stack (like in production),
# an exception is raised.
end
...@@ -68,9 +68,29 @@ Gitlab::Application.routes.draw do ...@@ -68,9 +68,29 @@ Gitlab::Application.routes.draw do
get :callback get :callback
get :jobs get :jobs
end end
resource :gitorious, only: [:create, :new], controller: :gitorious do
get :status
get :callback
get :jobs
end
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 # Explore area
......
# Projects # 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 ## List projects
Get a list of projects accessible by the authenticated user. Get a list of projects accessible by the authenticated user.
......
...@@ -166,8 +166,6 @@ git diff 6-0-stable:config/gitlab.yml.example 7-8-stable-ee:config/gitlab.yml.ex ...@@ -166,8 +166,6 @@ git diff 6-0-stable:config/gitlab.yml.example 7-8-stable-ee:config/gitlab.yml.ex
* 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/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/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. * 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 * Copy rack attack middleware config
```bash ```bash
...@@ -180,6 +178,12 @@ sudo -u git -H cp config/initializers/rack_attack.rb.example config/initializers ...@@ -180,6 +178,12 @@ sudo -u git -H cp config/initializers/rack_attack.rb.example config/initializers
sudo cp lib/support/logrotate/gitlab /etc/logrotate.d/gitlab 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 ## 9. Start application
sudo service gitlab start sudo service gitlab start
......
...@@ -75,8 +75,9 @@ git diff origin/7-6-stable:config/gitlab.yml.example origin/7-8-stable:config/gi ...@@ -75,8 +75,9 @@ git diff origin/7-6-stable:config/gitlab.yml.example origin/7-8-stable:config/gi
#### Change Nginx settings #### 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 * 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 * 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) #### Setup time zone (optional)
......
...@@ -24,16 +24,19 @@ Triggered when you push to the repository except when pushing tags. ...@@ -24,16 +24,19 @@ Triggered when you push to the repository except when pushing tags.
"project_id": 15, "project_id": 15,
"repository": { "repository": {
"name": "Diaspora", "name": "Diaspora",
"url": "git@example.com:diaspora.git", "url": "git@example.com:mike/diasporadiaspora.git",
"description": "", "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": [ "commits": [
{ {
"id": "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", "id": "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327",
"message": "Update Catalan translation to e38cb41.", "message": "Update Catalan translation to e38cb41.",
"timestamp": "2011-12-12T14:27:31+02:00", "timestamp": "2011-12-12T14:27:31+02:00",
"url": "http://example.com/diaspora/commits/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", "url": "http://example.com/mike/diaspora/commit/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327",
"author": { "author": {
"name": "Jordi Mallach", "name": "Jordi Mallach",
"email": "jordi@softcatala.org" "email": "jordi@softcatala.org"
...@@ -43,7 +46,7 @@ Triggered when you push to the repository except when pushing tags. ...@@ -43,7 +46,7 @@ Triggered when you push to the repository except when pushing tags.
"id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", "id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
"message": "fixed readme", "message": "fixed readme",
"timestamp": "2012-01-03T23:36:29+02:00", "timestamp": "2012-01-03T23:36:29+02:00",
"url": "http://example.com/diaspora/commits/da1560886d4f094c3e6c9ef40349f7d38b5d27d7", "url": "http://example.com/mike/diaspora/commit/da1560886d4f094c3e6c9ef40349f7d38b5d27d7",
"author": { "author": {
"name": "GitLab dev user", "name": "GitLab dev user",
"email": "gitlabdev@dv6700.(none)" "email": "gitlabdev@dv6700.(none)"
...@@ -72,8 +75,13 @@ Triggered when you create (or delete) tags to the repository. ...@@ -72,8 +75,13 @@ Triggered when you create (or delete) tags to the repository.
"name": "jsmith", "name": "jsmith",
"url": "ssh://git@example.com/jsmith/example.git", "url": "ssh://git@example.com/jsmith/example.git",
"description": "", "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
} }
``` ```
......
...@@ -106,24 +106,19 @@ Feature: Project Active Tab ...@@ -106,24 +106,19 @@ Feature: Project Active Tab
And no other sub tabs should be active And no other sub tabs should be active
And the active main tab should be Commits And the active main tab should be Commits
# Sub Tabs: Issues
Scenario: On Project Issues/Browse Scenario: On Project Issues/Browse
Given I visit my project's issues page Given I visit my project's issues page
Then the active sub tab should be Issues Then the active main tab should be Issues
And no other sub tabs should be active And no other main tabs should be active
And the active main tab should be Issues
Scenario: On Project Issues/Milestones Scenario: On Project Issues/Milestones
Given I visit my project's issues page Given I visit my project's issues page
And I click the "Milestones" tab And I click the "Milestones" tab
Then the active sub tab should be Milestones Then the active main tab should be Milestones
And no other sub tabs should be active And no other main tabs should be active
And the active main tab should be Issues
Scenario: On Project Issues/Labels Scenario: On Project Issues/Labels
Given I visit my project's issues page Given I visit my project's issues page
And I click the "Labels" tab And I click the "Labels" tab
Then the active sub tab should be Labels Then the active main tab should be Labels
And no other sub tabs should be active And no other main tabs should be active
And the active main tab should be Issues
...@@ -93,11 +93,11 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps ...@@ -93,11 +93,11 @@ class Spinach::Features::ProjectActiveTab < Spinach::FeatureSteps
ensure_active_sub_tab('Issues') ensure_active_sub_tab('Issues')
end end
step 'the active sub tab should be Milestones' do step 'the active main tab should be Milestones' do
ensure_active_sub_tab('Milestones') ensure_active_main_tab('Milestones')
end end
step 'the active sub tab should be Labels' do step 'the active main tab should be Labels' do
ensure_active_sub_tab('Labels') ensure_active_main_tab('Labels')
end end
end end
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 ...@@ -41,6 +41,9 @@ module Gitlab
url: project.url_to_repo, url: project.url_to_repo,
description: project.description, description: project.description,
homepage: project.web_url, 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: [], commits: [],
total_commits_count: commits_count total_commits_count: commits_count
......
## GitLab ## 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 two hashes (##) are comments with information.
## Lines starting with one hash (#) are configuration parameters that can be uncommented. ## Lines starting with one hash (#) are configuration parameters that can be uncommented.
...@@ -56,6 +56,37 @@ server { ...@@ -56,6 +56,37 @@ server {
try_files $uri $uri/index.html $uri.html @gitlab; 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, ## If a file, which is not found in the root folder is requested,
## then the proxy passes the request to the upsteam (gitlab unicorn). ## then the proxy passes the request to the upsteam (gitlab unicorn).
location @gitlab { location @gitlab {
......
## GitLab ## GitLab
## Contributors: randx, yin8086, sashkab, orkoden, axilleas, bbodenmiller ## Contributors: randx, yin8086, sashkab, orkoden, axilleas, bbodenmiller, DouweM
## ##
## Modified from nginx http version ## Modified from nginx http version
## Modified from http://blog.phusion.nl/2012/04/21/tutorial-setting-up-gitlab-on-debian-6/ ## Modified from http://blog.phusion.nl/2012/04/21/tutorial-setting-up-gitlab-on-debian-6/
...@@ -101,6 +101,38 @@ server { ...@@ -101,6 +101,38 @@ server {
try_files $uri $uri/index.html $uri.html @gitlab; 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, ## If a file, which is not found in the root folder is requested,
## then the proxy passes the request to the upsteam (gitlab unicorn). ## then the proxy passes the request to the upsteam (gitlab unicorn).
location @gitlab { 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 ...@@ -13,6 +13,9 @@ describe 'Gitlab::PushDataBuilder' do
it { expect(data[:after]).to eq('5937ac0a7beb003549fc5fd26fc247adbce4a52e') } it { expect(data[:after]).to eq('5937ac0a7beb003549fc5fd26fc247adbce4a52e') }
it { expect(data[:ref]).to eq('refs/heads/master') } it { expect(data[:ref]).to eq('refs/heads/master') }
it { expect(data[:commits].size).to eq(3) } 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) } it { expect(data[:total_commits_count]).to eq(3) }
end 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