Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into configure-protection

Conflicts:
	CHANGELOG
	db/schema.rb
This commit is contained in:
Marco Wessel 2015-01-30 00:16:24 +01:00
commit 20e269cb92
140 changed files with 1482 additions and 632 deletions

View File

@ -3,10 +3,10 @@ Note: The upcoming release contains empty lines to reduce the number of merge co
v 7.8.0
- 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
- Expose description in groups API
-
-
- Better UI for project services page
- Cleaner UI for web editor
- Add diff syntax highlighting in email-on-push service notifications (Hannes Rosenögger)
-
-
@ -17,8 +17,10 @@ v 7.8.0
- Show tags in commit view (Hannes Rosenögger)
- Only count a user's vote once on a merge request or issue (Michael Clarke)
-
- Increate font size when browse source files and diffs
- Create new file in empty repository using GitLab UI
-
-
- Ability to clone project using oauth2 token
-
- Upgrade Sidekiq gem to version 3.3.0
- Stop git zombie creation during force push check
@ -29,11 +31,13 @@ v 7.8.0
-
-
- Allow configuring protection of the default branch upon first push (Marco Wessel)
-
-
- Add a commit calendar to the user profile (Hannes Rosenögger)
-
-
-
-
-
- Fix long broadcast message cut-off on left sidebar (Visay Keo)
- Add Project Avatars (Steven Thonus and Hannes Rosenögger)
-
-
@ -59,6 +63,7 @@ v 7.8.0
-
-
-
- Added support for firing system hooks on group create/destroy and adding/removing users to group (Boyan Tabakov)
v 7.7.1
- Improve mention autocomplete performance

View File

@ -154,6 +154,9 @@ gem "slack-notifier", "~> 1.0.0"
# d3
gem "d3_rails", "~> 3.1.4"
#cal-heatmap
gem "cal-heatmap-rails", "~> 0.0.1"
# underscore-rails
gem "underscore-rails", "~> 1.4.4"
@ -170,7 +173,7 @@ gem 'ace-rails-ap'
gem 'mousetrap-rails'
# Semantic UI Sass for Sidebar
gem 'semantic-ui-sass', '~> 0.16.1.0'
gem 'semantic-ui-sass', '~> 1.8.0'
gem "sass-rails", '~> 4.0.2'
gem "coffee-rails"

View File

@ -52,6 +52,7 @@ GEM
sass (~> 3.2)
browser (0.7.2)
builder (3.2.2)
cal-heatmap-rails (0.0.1)
capybara (2.2.1)
mime-types (>= 1.16)
nokogiri (>= 1.3.3)
@ -490,7 +491,7 @@ GEM
activesupport (>= 3.1, < 4.2)
select2-rails (3.5.2)
thor (~> 0.14)
semantic-ui-sass (0.16.1.0)
semantic-ui-sass (1.8.0.0)
sass (~> 3.2)
settingslogic (2.0.9)
sexp_processor (4.4.0)
@ -627,6 +628,7 @@ DEPENDENCIES
binding_of_caller
bootstrap-sass (~> 3.0)
browser
cal-heatmap-rails (~> 0.0.1)
capybara (~> 2.2.1)
carrierwave
coffee-rails
@ -715,7 +717,7 @@ DEPENDENCIES
sdoc
seed-fu
select2-rails
semantic-ui-sass (~> 0.16.1.0)
semantic-ui-sass (~> 1.8.0)
settingslogic
shoulda-matchers (~> 2.1.0)
sidekiq (~> 3.3)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -39,6 +39,7 @@
#= require shortcuts_dashboard_navigation
#= require shortcuts_issueable
#= require shortcuts_network
#= require cal-heatmap
#= require_tree .
window.slugify = (text) ->

View File

@ -0,0 +1,44 @@
class @EditBlob
constructor: (assets_path, mode)->
ace.config.set "modePath", assets_path + '/ace'
ace.config.loadModule "ace/ext/searchbox"
if mode
ace_mode = mode
editor = ace.edit("editor")
editor.focus()
@editor = editor
if ace_mode
editor.getSession().setMode "ace/mode/" + ace_mode
disableButtonIfEmptyField "#commit_message", ".js-commit-button"
$(".js-commit-button").click ->
$("#file-content").val editor.getValue()
$(".file-editor form").submit()
return
editModePanes = $(".js-edit-mode-pane")
editModeLinks = $(".js-edit-mode a")
editModeLinks.click (event) ->
event.preventDefault()
currentLink = $(this)
paneId = currentLink.attr("href")
currentPane = editModePanes.filter(paneId)
editModeLinks.parent().removeClass "active hover"
currentLink.parent().addClass "active hover"
editModePanes.hide()
if paneId is "#preview"
currentPane.fadeIn 200
$.post currentLink.data("preview-url"),
content: editor.getValue()
, (response) ->
currentPane.empty().append response
return
else
currentPane.fadeIn 200
editor.focus()
return
editor: ->
return @editor

View File

@ -0,0 +1,21 @@
class @NewBlob
constructor: (assets_path, mode)->
ace.config.set "modePath", assets_path + '/ace'
ace.config.loadModule "ace/ext/searchbox"
if mode
ace_mode = mode
editor = ace.edit("editor")
editor.focus()
@editor = editor
if ace_mode
editor.getSession().setMode "ace/mode/" + ace_mode
disableButtonIfEmptyField "#commit_message", ".js-commit-button"
$(".js-commit-button").click ->
$("#file-content").val editor.getValue()
$(".file-editor form").submit()
return
editor: ->
return @editor

View File

@ -0,0 +1,35 @@
class @calendar
options =
month: "short"
day: "numeric"
year: "numeric"
constructor: (timestamps, starting_year, starting_month) ->
cal = new CalHeatMap()
cal.init
itemName: ["commit"]
data: timestamps
domain: "year"
subDomain: "month"
start: new Date(starting_year, starting_month)
domainLabelFormat: "%b"
id: "cal-heatmap"
domain: "month"
subDomain: "day"
range: 12
tooltip: true
domainDynamicDimension: false
colLimit: 4
label:
position: "top"
domainMargin: 1
legend: [
0
1
4
7
]
legendCellPadding: 3
onClick: (date, count) ->
return
return

View File

@ -9,17 +9,3 @@ class @ProjectNew
initEvents: ->
disableButtonIfEmptyField '#project_name', '.project-submit'
$('#project_issues_enabled').change ->
if ($(this).is(':checked') == true)
$('#project_issues_tracker').removeAttr('disabled')
else
$('#project_issues_tracker').attr('disabled', 'disabled')
$('#project_issues_tracker').change()
$('#project_issues_tracker').change ->
if ($(this).val() == gon.default_issues_tracker || $(this).is(':disabled'))
$('#project_issues_tracker_id').attr('disabled', 'disabled')
else
$('#project_issues_tracker_id').removeAttr('disabled')

View File

@ -6,7 +6,7 @@ class @ProjectShow
new Flash('Star toggle failed. Try again later.', 'alert')
$("a[data-toggle='tab']").on "shown.bs.tab", (e) ->
$.cookie "default_view", $(e.target).attr("href")
$.cookie "default_view", $(e.target).attr("href"), { expires: 30 }
defaultView = $.cookie("default_view")
if defaultView

View File

@ -8,6 +8,7 @@
*= require select2
*= require_self
*= require dropzone/basic
*= require cal-heatmap
*/
@import "main/*";

View File

@ -173,6 +173,11 @@
margin-right: 0px;
}
}
&.btn-lg {
font-size: 15px;
line-height: 1.4;
}
}
.btn-block {

View File

@ -0,0 +1,95 @@
.calendar_onclick_placeholder {
padding: 0 0 2px 0;
}
.calendar_commit_activity {
padding: 5px 0 0;
}
.calendar_onclick_second {
font-size: 14px;
display: block;
}
.calendar_onclick_hr {
padding: 0;
margin: 10px 0;
}
.calendar_commit_date {
color: #999;
}
.calendar_activity_summary {
font-size: 14px;
}
/**
* This overwrites the default values of the cal-heatmap gem
*/
.calendar {
.qi {
background-color: #999;
fill: #fff;
}
.q1 {
background-color: #dae289;
fill: #ededed;
}
.q2 {
background-color: #cedb9c;
fill: #ACD5F2;
}
.q3 {
background-color: #b5cf6b;
fill: #7FA8D1;
}
.q4 {
background-color: #637939;
fill: #49729B;
}
.q5 {
background-color: #3b6427;
fill: #254E77;
}
.domain-background {
fill: none;
shape-rendering: crispedges;
}
.ch-tooltip {
position: absolute;
display: none;
margin-top: 22px;
margin-left: 1px;
font-size: 13px;
padding: 3px;
font-weight: 550;
background-color: #222;
span {
position: absolute;
width: 200px;
text-align: center;
visibility: hidden;
border-radius: 10px;
&:after {
content: '';
position: absolute;
top: 100%;
left: 50%;
margin-left: -8px;
width: 0;
height: 0;
border-top: 8px solid #000000;
border-right: 8px solid transparent;
border-left: 8px solid transparent;
}
}
}
}

View File

@ -10,8 +10,8 @@
border: none;
border-radius: 0;
font-family: $monospace_font;
font-size: 12px !important;
line-height: 16px !important;
font-size: $code_font_size !important;
line-height: $code_line_height !important;
margin: 0;
overflow: auto;
overflow-y: hidden;
@ -38,8 +38,8 @@
a {
font-family: $monospace_font;
display: block;
font-size: 12px !important;
line-height: 16px !important;
font-size: $code_font_size !important;
line-height: $code_line_height !important;
white-space: nowrap;
i {

View File

@ -1,9 +1,6 @@
/*
* Twitter bootstrap with GitLab customizations/additions
*
* Some unused bootstrap compontents like panels are not included.
* Other components like tabs are modified to GitLab style.
*
*/
$font-size-base: 13px !default;

View File

@ -139,7 +139,7 @@
}
@mixin panel-colored {
border: none;
border: 1px solid #EEE;
background: $box_bg;
@include box-shadow(0 1px 1px rgba(0, 0, 0, 0.09));

View File

@ -59,3 +59,5 @@ $list-font-size: 15px;
$sidebar_width: 230px;
$avatar_radius: 50%;
$code_font_size: 13px;
$code_line_height: 1.5;

View File

@ -112,3 +112,7 @@
color: #FFF;
}
}
.dash-list .str-truncated {
max-width: 72%;
}

View File

@ -37,7 +37,7 @@
overflow-y: hidden;
background: #FFF;
color: #333;
font-size: 12px;
font-size: $code_font_size;
.old {
span.idiff {
background-color: #F99;
@ -64,8 +64,8 @@
margin: 0px;
padding: 0px;
td {
line-height: 18px;
font-size: 12px;
line-height: $code_line_height;
font-size: $code_font_size;
}
}

View File

@ -31,4 +31,26 @@
margin: 5px 8px 0 8px;
}
}
.file-title {
@extend .monospace;
font-size: 14px;
padding: 5px;
}
.editor-ref {
background: #f5f5f5;
padding: 11px 15px;
border-right: 1px solid #CCC;
display: inline-block;
margin: -5px -5px;
margin-right: 10px;
}
.editor-file-name {
.new-file-name {
display: inline-block;
width: 200px;
}
}
}

View File

@ -138,9 +138,10 @@ header {
top: -1px;
padding-right: 0px !important;
img {
width: 26px;
height: 26px;
@include border-radius($avatar_radius);
width: 50px;
height: 50px;
margin: -15px;
margin-left: 5px;
}
}

View File

@ -163,8 +163,9 @@ form.edit-issue {
}
}
.issue-title {
h3.issue-title {
margin-top: 0;
font-size: 2em;
}
.context .select2-container {

View File

@ -122,6 +122,7 @@
background: $box_bg;
margin-bottom: 20px;
color: #666;
border: 1px solid #EEE;
@include box-shadow(0 1px 1px rgba(0, 0, 0, 0.09));
.ci_widget {

View File

@ -16,6 +16,8 @@
.project-home-panel {
margin-bottom: 15px;
position: relative;
padding-left: 85px;
&.empty-project {
border-bottom: 0px;
@ -23,6 +25,22 @@
margin-bottom: 0px;
}
.project-identicon-holder {
position: absolute;
left: 0;
.avatar {
width: 70px;
height: 70px;
@include border-radius(0px);
}
.identicon {
font-size: 45px;
line-height: 1.6;
}
}
.project-home-dropdown {
margin-left: 10px;
float: right;

View File

@ -181,7 +181,7 @@ class ApplicationController < ActionController::Base
end
def add_gon_variables
gon.default_issues_tracker = Project.issues_tracker.default_value
gon.default_issues_tracker = Project.new.default_issue_tracker.to_param
gon.api_version = API::API.version
gon.relative_url_root = Gitlab.config.gitlab.relative_url_root
gon.default_avatar_url = URI::join(Gitlab.config.gitlab.url, ActionController::Base.helpers.image_path('no_avatar.png')).to_s

View File

@ -1,7 +0,0 @@
class Projects::BaseTreeController < Projects::ApplicationController
include ExtractsPath
before_filter :authorize_download_code!
before_filter :require_non_empty_project
end

View File

@ -2,7 +2,7 @@
class Projects::BlameController < Projects::ApplicationController
include ExtractsPath
# Authorize
before_filter :assign_ref_vars
before_filter :authorize_download_code!
before_filter :require_non_empty_project

View File

@ -2,16 +2,70 @@
class Projects::BlobController < Projects::ApplicationController
include ExtractsPath
# Authorize
before_filter :authorize_download_code!
before_filter :require_non_empty_project
before_filter :authorize_push_code!, only: [:destroy]
# Raised when given an invalid file path
class InvalidPathError < StandardError; end
before_filter :blob
before_filter :authorize_download_code!
before_filter :require_non_empty_project, except: [:new, :create]
before_filter :authorize_push_code!, only: [:destroy]
before_filter :assign_blob_vars
before_filter :commit, except: [:new, :create]
before_filter :blob, except: [:new, :create]
before_filter :from_merge_request, only: [:edit, :update]
before_filter :after_edit_path, only: [:edit, :update]
before_filter :require_branch_head, only: [:edit, :update]
def new
commit unless @repository.empty?
end
def create
file_path = File.join(@path, File.basename(params[:file_name]))
result = Files::CreateService.new(@project, current_user, params, @ref, file_path).execute
if result[:status] == :success
flash[:notice] = "Your changes have been successfully committed"
redirect_to project_blob_path(@project, File.join(@ref, file_path))
else
flash[:alert] = result[:message]
render :new
end
end
def show
end
def edit
@last_commit = Gitlab::Git::Commit.last_for_path(@repository, @ref, @path).sha
end
def update
result = Files::UpdateService.
new(@project, current_user, params, @ref, @path).execute
if result[:status] == :success
flash[:notice] = "Your changes have been successfully committed"
if from_merge_request
from_merge_request.reload_code
end
redirect_to after_edit_path
else
flash[:alert] = result[:message]
render :edit
end
end
def preview
@content = params[:content]
diffy = Diffy::Diff.new(@blob.data, @content, diff: '-U 3',
include_diff_info: true)
@diff_lines = Gitlab::Diff::Parser.new.parse(diffy.diff.scan(/.*\n/))
render layout: false
end
def destroy
result = Files::DeleteService.new(@project, current_user, params, @ref, @path).execute
@ -46,10 +100,44 @@ class Projects::BlobController < Projects::ApplicationController
if @blob
@blob
elsif tree.entries.any?
redirect_to project_tree_path(@project, File.join(@ref, @path)) and return
else
if tree = @repository.tree(@commit.id, @path)
if tree.entries.any?
redirect_to project_tree_path(@project, File.join(@ref, @path)) and return
end
end
return not_found!
end
end
def commit
@commit = @repository.commit(@ref)
return not_found! unless @commit
end
def assign_blob_vars
@id = params[:id]
@ref, @path = extract_ref(@id)
rescue InvalidPathError
not_found!
end
def after_edit_path
@after_edit_path ||=
if from_merge_request
diffs_project_merge_request_path(from_merge_request.target_project, from_merge_request) +
"#file-path-#{hexdigest(@path)}"
else
project_blob_path(@project, @id)
end
end
def from_merge_request
# If blob edit was initiated from merge request page
@from_merge_request ||= MergeRequest.find_by(id: params[:from_merge_request_id])
end
end

View File

@ -3,7 +3,7 @@ require "base64"
class Projects::CommitsController < Projects::ApplicationController
include ExtractsPath
# Authorize
before_filter :assign_ref_vars
before_filter :authorize_download_code!
before_filter :require_non_empty_project

View File

@ -1,60 +0,0 @@
class Projects::EditTreeController < Projects::BaseTreeController
before_filter :require_branch_head
before_filter :blob
before_filter :authorize_push_code!
before_filter :from_merge_request
before_filter :after_edit_path
def show
@last_commit = Gitlab::Git::Commit.last_for_path(@repository, @ref, @path).sha
end
def update
result = Files::UpdateService.
new(@project, current_user, params, @ref, @path).execute
if result[:status] == :success
flash[:notice] = "Your changes have been successfully committed"
if from_merge_request
from_merge_request.reload_code
end
redirect_to after_edit_path
else
flash[:alert] = result[:message]
render :show
end
end
def preview
@content = params[:content]
diffy = Diffy::Diff.new(@blob.data, @content, diff: '-U 3',
include_diff_info: true)
@diff_lines = Gitlab::Diff::Parser.new.parse(diffy.diff.scan(/.*\n/))
render layout: false
end
private
def blob
@blob ||= @repository.blob_at(@commit.id, @path)
end
def after_edit_path
@after_edit_path ||=
if from_merge_request
diffs_project_merge_request_path(from_merge_request.target_project, from_merge_request) +
"#file-path-#{hexdigest(@path)}"
else
project_blob_path(@project, @id)
end
end
def from_merge_request
# If blob edit was initiated from merge request page
@from_merge_request ||= MergeRequest.find_by(id: params[:from_merge_request_id])
end
end

View File

@ -2,7 +2,7 @@ class Projects::NetworkController < Projects::ApplicationController
include ExtractsPath
include ApplicationHelper
# Authorize
before_filter :assign_ref_vars
before_filter :authorize_download_code!
before_filter :require_non_empty_project

View File

@ -1,20 +0,0 @@
class Projects::NewTreeController < Projects::BaseTreeController
before_filter :require_branch_head
before_filter :authorize_push_code!
def show
end
def update
file_path = File.join(@path, File.basename(params[:file_name]))
result = Files::CreateService.new(@project, current_user, params, @ref, file_path).execute
if result[:status] == :success
flash[:notice] = "Your changes have been successfully committed"
redirect_to project_blob_path(@project, File.join(@ref, file_path))
else
flash[:alert] = result[:message]
render :show
end
end
end

View File

@ -2,7 +2,7 @@
class Projects::RawController < Projects::ApplicationController
include ExtractsPath
# Authorize
before_filter :assign_ref_vars
before_filter :authorize_download_code!
before_filter :require_non_empty_project

View File

@ -1,7 +1,7 @@
class Projects::RefsController < Projects::ApplicationController
include ExtractsPath
# Authorize
before_filter :assign_ref_vars
before_filter :authorize_download_code!
before_filter :require_non_empty_project

View File

@ -9,7 +9,7 @@ class Projects::ServicesController < Projects::ApplicationController
def index
@project.build_missing_services
@services = @project.services.reload
@services = @project.services.visible.reload
end
def edit
@ -17,7 +17,8 @@ class Projects::ServicesController < Projects::ApplicationController
def update
if @service.update_attributes(service_params)
redirect_to edit_project_service_path(@project, @service.to_param)
redirect_to edit_project_service_path(@project, @service.to_param),
notice: 'Successfully updated.'
else
render 'edit'
end
@ -45,7 +46,8 @@ class Projects::ServicesController < Projects::ApplicationController
:title, :token, :type, :active, :api_key, :subdomain,
:room, :recipients, :project_url, :webhook,
:user_key, :device, :priority, :sound, :bamboo_url, :username, :password,
:build_key, :server, :teamcity_url, :build_type
:build_key, :server, :teamcity_url, :build_type,
:description, :issues_url, :new_issue_url
)
end
end

View File

@ -1,7 +1,12 @@
# Controller for viewing a repository's file structure
class Projects::TreeController < Projects::BaseTreeController
def show
class Projects::TreeController < Projects::ApplicationController
include ExtractsPath
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?
if @repository.blob_at(@commit.id, @path)
redirect_to project_blob_path(@project, File.join(@ref, @path)) and return

View File

@ -1,16 +1,12 @@
class UsersController < ApplicationController
skip_before_filter :authenticate_user!, only: [:show]
skip_before_filter :authenticate_user!
before_filter :set_user
layout :determine_layout
def show
@user = User.find_by_username!(params[:username])
unless current_user || @user.public_profile?
return authenticate_user!
end
# Projects user can view
authorized_projects_ids = ProjectsFinder.new.execute(current_user).pluck(:id)
visible_projects = ProjectsFinder.new.execute(current_user)
authorized_projects_ids = visible_projects.pluck(:id)
@projects = @user.personal_projects.
where(id: authorized_projects_ids)
@ -30,6 +26,19 @@ class UsersController < ApplicationController
end
end
def calendar
visible_projects = ProjectsFinder.new.execute(current_user)
# Get user repositories and collect timestamps for commits
user_repositories = visible_projects.map(&:repository)
calendar = Gitlab::CommitsCalendar.new(user_repositories, @user)
@timestamps = calendar.timestamps
@starting_year = (Time.now - 1.year).strftime("%Y")
@starting_month = Date.today.strftime("%m").to_i
render 'calendar', layout: false
end
def determine_layout
if current_user
'navless'
@ -37,4 +46,14 @@ class UsersController < ApplicationController
'public_users'
end
end
private
def set_user
@user = User.find_by_username!(params[:username])
unless current_user || @user.public_profile?
return authenticate_user!
end
end
end

View File

@ -56,8 +56,6 @@ module ApplicationHelper
image_tag project.avatar.url, options
elsif project.avatar_in_git
image_tag project_avatar_path(project), options
elsif options[:only_uploaded]
image_tag '/assets/no_project_icon.png', options
else # generated icon
project_identicon(project, options)
end

View File

@ -19,4 +19,42 @@ module BlobHelper
def no_highlight_files
%w(credits changelog copying copyright license authors)
end
def edit_blob_link(project, ref, path, options = {})
blob =
begin
project.repository.blob_at(ref, path)
rescue
nil
end
if blob && blob.text?
text = 'Edit'
after = options[:after] || ''
from_mr = options[:from_merge_request_id]
link_opts = {}
link_opts[:from_merge_request_id] = from_mr if from_mr
cls = 'btn btn-small'
if allowed_tree_edit?(project, ref)
link_to text, project_edit_blob_path(project, tree_join(ref, path),
link_opts), class: cls
else
content_tag :span, text, class: cls + ' disabled'
end + after.html_safe
else
''
end
end
def leave_edit_message
"Leave edit mode?\nAll unsaved changes will be lost."
end
def editing_preview_title(filename)
if Gitlab::MarkdownHelper.previewable?(filename)
'Preview'
else
'Preview changes'
end
end
end

View File

@ -16,45 +16,25 @@ module IssuesHelper
def url_for_project_issues(project = @project)
return '' if project.nil?
if project.used_default_issues_tracker? || !external_issues_tracker_enabled?
project_issues_path(project)
else
url = Gitlab.config.issues_tracker[project.issues_tracker]['project_url']
url.gsub(':project_id', project.id.to_s).
gsub(':issues_tracker_id', project.issues_tracker_id.to_s)
end
project.issues_tracker.project_url
end
def url_for_new_issue(project = @project)
return '' if project.nil?
if project.used_default_issues_tracker? || !external_issues_tracker_enabled?
url = new_project_issue_path project_id: project
else
issues_tracker = Gitlab.config.issues_tracker[project.issues_tracker]
url = issues_tracker['new_issue_url']
url.gsub(':project_id', project.id.to_s).
gsub(':issues_tracker_id', project.issues_tracker_id.to_s)
end
project.issues_tracker.new_issue_url
end
def url_for_issue(issue_iid, project = @project)
return '' if project.nil?
if project.used_default_issues_tracker? || !external_issues_tracker_enabled?
url = project_issue_url project_id: project, id: issue_iid
else
url = Gitlab.config.issues_tracker[project.issues_tracker]['issues_url']
url.gsub(':id', issue_iid.to_s).
gsub(':project_id', project.id.to_s).
gsub(':issues_tracker_id', project.issues_tracker_id.to_s)
end
project.issues_tracker.issue_url(issue_iid)
end
def title_for_issue(issue_iid, project = @project)
return '' if project.nil?
if project.used_default_issues_tracker?
if project.default_issues_tracker?
issue = project.issues.where(iid: issue_iid).first
return issue.title if issue
end
@ -77,11 +57,6 @@ module IssuesHelper
ts.html_safe
end
# Checks if issues_tracker setting exists in gitlab.yml
def external_issues_tracker_enabled?
Gitlab.config.issues_tracker && Gitlab.config.issues_tracker.values.any?
end
def bulk_update_milestone_options
options_for_select(['None (backlog)']) +
options_from_collection_for_select(project_active_milestones, 'id',

View File

@ -72,18 +72,6 @@ module ProjectsHelper
@project.milestones.active.order("due_date, title ASC")
end
def project_issues_trackers(current_tracker = nil)
values = Project.issues_tracker.values.map do |tracker_key|
if tracker_key.to_sym == :gitlab
['GitLab', tracker_key]
else
[Gitlab.config.issues_tracker[tracker_key]['title'] || tracker_key, tracker_key]
end
end
options_for_select(values, current_tracker)
end
def link_to_toggle_star(title, starred, signed_in)
cls = 'star-btn'
cls << ' disabled' unless signed_in
@ -187,7 +175,13 @@ module ProjectsHelper
"Issues - " + title
end
elsif current_controller?(:blob)
"#{@project.path}\/#{@blob.path} at #{@ref} - " + title
if current_action?(:new) || current_action?(:create)
"New file at #{@ref}"
elsif current_action?(:show)
"#{@blob.path} at #{@ref}"
elsif @blob
"Edit file #{@blob.path} at #{@ref}"
end
elsif current_controller?(:commits)
"Commits at #{@ref} - " + title
elsif current_controller?(:merge_requests)
@ -257,7 +251,7 @@ module ProjectsHelper
end
def github_import_enabled?
Gitlab.config.omniauth.enabled && enabled_oauth_providers.include?(:github)
enabled_oauth_providers.include?(:github)
end
end

View File

@ -64,32 +64,6 @@ module TreeHelper
::Gitlab::GitAccess.can_push_to_branch?(current_user, project, ref)
end
def edit_blob_link(project, ref, path, options = {})
blob =
begin
project.repository.blob_at(ref, path)
rescue
nil
end
if blob && blob.text?
text = 'Edit'
after = options[:after] || ''
from_mr = options[:from_merge_request_id]
link_opts = {}
link_opts[:from_merge_request_id] = from_mr if from_mr
cls = 'btn btn-small'
if allowed_tree_edit?(project, ref)
link_to text, project_edit_tree_path(project, tree_join(ref, path),
link_opts), class: cls
else
content_tag :span, text, class: cls + ' disabled'
end + after.html_safe
else
''
end
end
def tree_breadcrumbs(tree, max_links = 2)
if @path.present?
part_path = ""
@ -121,16 +95,4 @@ module TreeHelper
return tree.name
end
end
def leave_edit_message
"Leave edit mode?\nAll unsaved changes will be lost."
end
def editing_preview_title(filename)
if Gitlab::MarkdownHelper.previewable?(filename)
'Preview'
else
'Diff'
end
end
end

View File

@ -25,6 +25,9 @@ class Group < Namespace
mount_uploader :avatar, AttachmentUploader
after_create :post_create_hook
after_destroy :post_destroy_hook
def human_name
name
end
@ -74,6 +77,18 @@ class Group < Namespace
projects.public_only.any?
end
def post_create_hook
system_hook_service.execute_hooks_for(self, :create)
end
def post_destroy_hook
system_hook_service.execute_hooks_for(self, :destroy)
end
def system_hook_service
SystemHooksService.new
end
class << self
def search(query)
where("LOWER(namespaces.name) LIKE :query", query: "%#{query.downcase}%")

View File

@ -27,8 +27,9 @@ class GroupMember < Member
scope :with_group, ->(group) { where(source_id: group.id) }
scope :with_user, ->(user) { where(user_id: user.id) }
after_create :notify_create
after_create :post_create_hook
after_update :notify_update
after_destroy :post_destroy_hook
def self.access_level_roles
Gitlab::Access.options_with_owner
@ -42,8 +43,9 @@ class GroupMember < Member
access_level
end
def notify_create
def post_create_hook
notification_service.new_group_member(self)
system_hook_service.execute_hooks_for(self, :create)
end
def notify_update
@ -52,6 +54,14 @@ class GroupMember < Member
end
end
def post_destroy_hook
system_hook_service.execute_hooks_for(self, :destroy)
end
def system_hook_service
SystemHooksService.new
end
def notification_service
NotificationService.new
end

View File

@ -74,7 +74,13 @@ class Project < ActiveRecord::Base
has_one :bamboo_service, dependent: :destroy
has_one :teamcity_service, dependent: :destroy
has_one :pushover_service, dependent: :destroy
has_one :forked_project_link, dependent: :destroy, foreign_key: 'forked_to_project_id'
has_one :jira_service, dependent: :destroy
has_one :redmine_service, dependent: :destroy
has_one :custom_issue_tracker_service, dependent: :destroy
has_one :gitlab_issue_tracker_service, dependent: :destroy
has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id"
has_one :forked_from_project, through: :forked_project_link
# Merge Requests for target project should be removed with it
has_many :merge_requests, dependent: :destroy, foreign_key: 'target_project_id'
@ -144,8 +150,6 @@ class Project < ActiveRecord::Base
scope :public_and_internal_only, -> { where(visibility_level: Project.public_and_internal_levels) }
scope :non_archived, -> { where(archived: false) }
enumerize :issues_tracker, in: (Gitlab.config.issues_tracker.keys).append(:gitlab), default: :gitlab
state_machine :import_status, initial: :none do
event :import_start do
transition [:none, :finished] => :started
@ -305,19 +309,43 @@ class Project < ActiveRecord::Base
end
def issue_exists?(issue_id)
if used_default_issues_tracker?
if default_issues_tracker?
self.issues.where(iid: issue_id).first.present?
else
true
end
end
def used_default_issues_tracker?
self.issues_tracker == Project.issues_tracker.default_value
def default_issue_tracker
gitlab_issue_tracker_service ||= create_gitlab_issue_tracker_service
end
def issues_tracker
if external_issue_tracker
external_issue_tracker
else
default_issue_tracker
end
end
def default_issues_tracker?
if external_issue_tracker
false
else
true
end
end
def external_issues_trackers
services.select(&:issue_tracker?).reject(&:default?)
end
def external_issue_tracker
@external_issues_tracker ||= external_issues_trackers.select(&:activated?).first
end
def can_have_issues_tracker_id?
self.issues_enabled && !self.used_default_issues_tracker?
self.issues_enabled && !self.default_issues_tracker?
end
def build_missing_services
@ -332,7 +360,7 @@ class Project < ActiveRecord::Base
def available_services_names
%w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla
emails_on_push gemnasium slack pushover buildbox bamboo teamcity)
emails_on_push gemnasium slack pushover buildbox bamboo teamcity jira redmine custom_issue_tracker)
end
def gitlab_ci?

View File

@ -0,0 +1,38 @@
class CustomIssueTrackerService < IssueTrackerService
prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
def title
if self.properties && self.properties['title'].present?
self.properties['title']
else
'Custom Issue Tracker'
end
end
def description
if self.properties && self.properties['description'].present?
self.properties['description']
else
'Custom issue tracker'
end
end
def to_param
'custom_issue_tracker'
end
def fields
[
{ type: 'text', name: 'title', placeholder: title },
{ type: 'text', name: 'description', placeholder: description },
{ type: 'text', name: 'project_url', placeholder: 'Project url' },
{ type: 'text', name: 'issues_url', placeholder: 'Issue url'},
{ type: 'text', name: 'new_issue_url', placeholder: 'New Issue url'}
]
end
def initialize_properties
self.properties = {} if properties.nil?
end
end

View File

@ -0,0 +1,25 @@
class GitlabIssueTrackerService < IssueTrackerService
include Rails.application.routes.url_helpers
prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
def default?
true
end
def to_param
'gitlab'
end
def project_url
project_issues_path(project)
end
def new_issue_url
new_project_issue_path project_id: project
end
def issue_url(iid)
"#{Gitlab.config.gitlab.url}#{project_issue_path(project_id: project, id: iid)}"
end
end

View File

@ -0,0 +1,74 @@
class IssueTrackerService < Service
validates :project_url, :issues_url, :new_issue_url, presence: true, if: :activated?
def category
:issue_tracker
end
def default?
false
end
def project_url
# implement inside child
end
def issues_url
# implement inside child
end
def new_issue_url
# implement inside child
end
def issue_url(iid)
self.issues_url.gsub(':id', iid.to_s)
end
def fields
[
{ type: 'text', name: 'description', placeholder: description },
{ type: 'text', name: 'project_url', placeholder: 'Project url' },
{ type: 'text', name: 'issues_url', placeholder: 'Issue url'},
{ type: 'text', name: 'new_issue_url', placeholder: 'New Issue url'}
]
end
def initialize_properties
if properties.nil?
if enabled_in_gitlab_config
self.properties = {
title: issues_tracker['title'],
project_url: set_project_url,
issues_url: issues_tracker['issues_url'],
new_issue_url: issues_tracker['new_issue_url']
}
else
self.properties = {}
end
end
end
private
def enabled_in_gitlab_config
Gitlab.config.issues_tracker &&
Gitlab.config.issues_tracker.values.any? &&
issues_tracker
end
def issues_tracker
Gitlab.config.issues_tracker[to_param]
end
def set_project_url
id = self.project.issues_tracker_id
if id
issues_tracker['project_url'].gsub(":issues_tracker_id", id)
else
issues_tracker['project_url']
end
end
end

View File

@ -0,0 +1,24 @@
class JiraService < IssueTrackerService
prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
def title
if self.properties && self.properties['title'].present?
self.properties['title']
else
'JIRA'
end
end
def description
if self.properties && self.properties['description'].present?
self.properties['description']
else
'Jira issue tracker'
end
end
def to_param
'jira'
end
end

View File

@ -0,0 +1,24 @@
class RedmineService < IssueTrackerService
prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
def title
if self.properties && self.properties['title'].present?
self.properties['title']
else
'Redmine'
end
end
def description
if self.properties && self.properties['description'].present?
self.properties['description']
else
'Redmine issue tracker'
end
end
def to_param
'redmine'
end
end

View File

@ -139,8 +139,10 @@ class Repository
def graph_log
Rails.cache.fetch(cache_key(:graph_log)) do
commits = raw_repository.log(limit: 6000, skip_merges: true,
commits = raw_repository.log(limit: 6000,
skip_merges: true,
ref: root_ref)
commits.map do |rugged_commit|
commit = Gitlab::Git::Commit.new(rugged_commit)
@ -148,12 +150,32 @@ class Repository
author_name: commit.author_name.force_encoding('UTF-8'),
author_email: commit.author_email.force_encoding('UTF-8'),
additions: commit.stats.additions,
deletions: commit.stats.deletions
deletions: commit.stats.deletions,
}
end
end
end
def timestamps_by_user_log(user)
args = %W(git log --author=#{user.email} --since=#{(Date.today - 1.year).to_s} --pretty=format:%cd --date=short)
dates = Gitlab::Popen.popen(args, path_to_repo).first.split("\n")
if dates.present?
dates
else
[]
end
end
def commits_per_day_for_user(user)
timestamps_by_user_log(user).
group_by { |commit_date| commit_date }.
inject({}) do |hash, (timestamp_date, commits)|
hash[timestamp_date] = commits.count
hash
end
end
def cache_key(type)
"#{type}:#{path_with_namespace}"
end

View File

@ -26,6 +26,8 @@ class Service < ActiveRecord::Base
validates :project_id, presence: true
scope :visible, -> { where.not(type: 'GitlabIssueTrackerService') }
def activated?
active
end
@ -86,4 +88,12 @@ class Service < ActiveRecord::Base
def async_execute(data)
Sidekiq::Client.enqueue(ProjectServiceWorker, id, data)
end
def issue_tracker?
self.category == :issue_tracker
end
def self.issue_tracker_service_list
Service.select(&:issue_tracker?).map{ |s| s.to_param }
end
end

View File

@ -9,10 +9,6 @@ module Files
return error("You are not allowed to create file in this branch")
end
unless repository.branch_names.include?(ref)
return error("You can only create files if you are on top of a branch")
end
file_name = File.basename(path)
file_path = path
@ -23,12 +19,21 @@ module Files
)
end
blob = repository.blob_at_branch(ref, file_path)
if project.empty_repo?
# everything is ok because repo does not have a commits yet
else
unless repository.branch_names.include?(ref)
return error("You can only create files if you are on top of a branch")
end
if blob
return error("Your changes could not be committed, because file with such name exists")
blob = repository.blob_at_branch(ref, file_path)
if blob
return error("Your changes could not be committed, because file with such name exists")
end
end
new_file_action = Gitlab::Satellite::NewFileAction.new(current_user, project, ref, file_path)
created_successfully = new_file_action.commit!(
params[:content],

View File

@ -2,9 +2,9 @@ module Issues
class CloseService < Issues::BaseService
def execute(issue, commit = nil)
if issue.close
notification_service.close_issue(issue, current_user)
event_service.close_issue(issue, current_user)
create_note(issue, commit)
notification_service.close_issue(issue, current_user)
execute_hooks(issue, 'close')
end

View File

@ -23,8 +23,8 @@ module Issues
end
if issue.previous_changes.include?('assignee_id')
notification_service.reassigned_issue(issue, current_user)
create_assignee_note(issue)
notification_service.reassigned_issue(issue, current_user)
end
issue.notice_added_references(issue.project, current_user)

View File

@ -11,9 +11,9 @@ module MergeRequests
if Gitlab::Satellite::MergeAction.new(current_user, merge_request).merge!(commit_message)
merge_request.merge
notification_service.merge_mr(merge_request, current_user)
create_merge_event(merge_request, current_user)
create_note(merge_request)
notification_service.merge_mr(merge_request, current_user)
execute_hooks(merge_request)
true

View File

@ -7,8 +7,8 @@ module MergeRequests
if merge_request.close
event_service.close_mr(merge_request, current_user)
notification_service.close_mr(merge_request, current_user)
create_note(merge_request)
notification_service.close_mr(merge_request, current_user)
execute_hooks(merge_request, 'close')
end

View File

@ -9,9 +9,9 @@ module MergeRequests
def execute(merge_request, commit_message)
merge_request.merge
notification_service.merge_mr(merge_request, current_user)
create_merge_event(merge_request, current_user)
create_note(merge_request)
notification_service.merge_mr(merge_request, current_user)
execute_hooks(merge_request, 'merge')
true

View File

@ -3,8 +3,8 @@ module MergeRequests
def execute(merge_request)
if merge_request.reopen
event_service.reopen_mr(merge_request, current_user)
notification_service.reopen_mr(merge_request, current_user)
create_note(merge_request)
notification_service.reopen_mr(merge_request, current_user)
execute_hooks(merge_request, 'reopen')
merge_request.reload_code
merge_request.mark_as_unchecked

View File

@ -33,8 +33,8 @@ module MergeRequests
end
if merge_request.previous_changes.include?('assignee_id')
notification_service.reassigned_merge_request(merge_request, current_user)
create_assignee_note(merge_request)
notification_service.reassigned_merge_request(merge_request, current_user)
end
merge_request.notice_added_references(merge_request.project, current_user)

View File

@ -314,15 +314,7 @@ class NotificationService
end
def new_resource_email(target, project, method)
if target.respond_to?(:participants)
recipients = target.participants
else
recipients = []
end
recipients = reject_muted_users(recipients, project)
recipients = reject_mention_users(recipients, project)
recipients = recipients.concat(project_watchers(project)).uniq
recipients = build_recipients(target, project)
recipients.delete(target.author)
recipients.each do |recipient|
@ -331,9 +323,7 @@ class NotificationService
end
def close_resource_email(target, project, current_user, method)
recipients = reject_muted_users([target.author, target.assignee], project)
recipients = reject_mention_users(recipients, project)
recipients = recipients.concat(project_watchers(project)).uniq
recipients = build_recipients(target, project)
recipients.delete(current_user)
recipients.each do |recipient|
@ -343,17 +333,7 @@ class NotificationService
def reassign_resource_email(target, project, current_user, method)
assignee_id_was = previous_record(target, "assignee_id")
recipients = User.where(id: [target.assignee_id, assignee_id_was])
# Add watchers to email list
recipients = recipients.concat(project_watchers(project))
# reject users with disabled notifications
recipients = reject_muted_users(recipients, project)
recipients = reject_mention_users(recipients, project)
# Reject me from recipients if I reassign an item
recipients = build_recipients(target, project)
recipients.delete(current_user)
recipients.each do |recipient|
@ -362,9 +342,7 @@ class NotificationService
end
def reopen_resource_email(target, project, current_user, method, status)
recipients = reject_muted_users([target.author, target.assignee], project)
recipients = reject_mention_users(recipients, project)
recipients = recipients.concat(project_watchers(project)).uniq
recipients = build_recipients(target, project)
recipients.delete(current_user)
recipients.each do |recipient|
@ -372,6 +350,20 @@ class NotificationService
end
end
def build_recipients(target, project)
recipients =
if target.respond_to?(:participants)
target.participants
else
[target.author, target.assignee]
end
recipients = reject_muted_users(recipients, project)
recipients = reject_mention_users(recipients, project)
recipients = recipients.concat(project_watchers(project)).uniq
recipients
end
def mailer
Notify.delay
end

View File

@ -60,6 +60,26 @@ class SystemHooksService
access_level: model.human_access,
project_visibility: Project.visibility_levels.key(model.project.visibility_level_field).downcase
})
when Group
owner = model.owner
data.merge!(
name: model.name,
path: model.path,
group_id: model.id,
owner_name: owner.respond_to?(:name) ? owner.name : nil,
owner_email: owner.respond_to?(:email) ? owner.email : nil,
)
when GroupMember
data.merge!(
group_name: model.group.name,
group_path: model.group.path,
group_id: model.group.id,
user_name: model.user.name,
user_email: model.user.email,
user_id: model.user.id,
group_access: model.human_access,
)
end
end
@ -68,6 +88,9 @@ class SystemHooksService
when ProjectMember
return "user_add_to_team" if event == :create
return "user_remove_from_team" if event == :destroy
when GroupMember
return 'user_add_to_group' if event == :create
return 'user_remove_from_group' if event == :destroy
else
"#{model.class.name.downcase}_#{event.to_s}"
end

View File

@ -32,7 +32,7 @@
%span.light.pull-right
= Milestone.count
%p
Active users last 30 days
Users who signed in during last 30 days
%span.light.pull-right
= User.where("current_sign_in_at > ?", 30.days.ago).count
.col-md-4

View File

@ -11,5 +11,4 @@
- elsif event.note?
= render "events/event/note", event: event
- else
= render "events/event/common", event: event
= render "events/event/common", event: event

View File

@ -5,7 +5,11 @@
.form-group
= f.label :access_level, "Group Access", class: 'control-label'
.col-sm-10= select_tag :access_level, options_for_select(GroupMember.access_level_roles, @users_group.access_level), class: "project-access-select select2"
.col-sm-10
= select_tag :access_level, options_for_select(GroupMember.access_level_roles, @users_group.access_level), class: "project-access-select select2"
.help-block
Read more about role permissions
%strong= link_to "here", help_page_path("permissions", "permissions"), class: "vlink"
.form-actions
= f.submit 'Add users into group', class: "btn btn-create"

View File

@ -1,11 +1,11 @@
%ul.sidebar-subnav
= nav_link(path: 'groups#edit') do
= link_to edit_group_path(@group) do
= link_to edit_group_path(@group), title: 'Group' do
%i.fa.fa-pencil-square-o
%span
Group
= nav_link(path: 'groups#projects') do
= link_to projects_group_path(@group) do
= link_to projects_group_path(@group), title: 'Projects' do
%i.fa.fa-folder
%span
Projects

View File

@ -43,6 +43,6 @@
%i.fa.fa-sign-out
%li.hidden-xs
= link_to current_user, class: "profile-pic", id: 'profile-pic' do
= image_tag avatar_icon(current_user.email, 26), alt: 'User activity'
= image_tag avatar_icon(current_user.email, 60), alt: 'User activity'
= render 'shared/outdated_browser'

View File

@ -1,5 +1,6 @@
- if defined?(sidebar)
.page-with-sidebar
= render "layouts/broadcast"
.sidebar-wrapper
= render(sidebar)
.content-wrapper

View File

@ -2,6 +2,5 @@
%html{ lang: "en"}
= render "layouts/head", title: "Admin area"
%body{class: "#{app_theme} #{theme_type} admin", :'data-page' => body_data_page}
= render "layouts/broadcast"
= render "layouts/head_panel", title: "Admin area"
= render 'layouts/page', sidebar: 'layouts/nav/admin'

View File

@ -2,6 +2,5 @@
%html{ lang: "en"}
= render "layouts/head", title: "Dashboard"
%body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page }
= render "layouts/broadcast"
= render "layouts/head_panel", title: "Dashboard"
= render 'layouts/page', sidebar: 'layouts/nav/dashboard'

View File

@ -2,6 +2,5 @@
%html{ lang: "en"}
= render "layouts/head", title: group_head_title
%body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page}
= render "layouts/broadcast"
= render "layouts/head_panel", title: @group.name
= render 'layouts/page', sidebar: 'layouts/nav/group'

View File

@ -5,49 +5,50 @@
%span
Overview
= nav_link(controller: :projects) do
= link_to admin_projects_path do
= link_to admin_projects_path, title: 'Projects' do
%i.fa.fa-cube
%span
Projects
= nav_link(controller: :users) do
= link_to admin_users_path do
= link_to admin_users_path, title: 'Users' do
%i.fa.fa-user
%span
Users
= nav_link(controller: :groups) do
= link_to admin_groups_path do
= link_to admin_groups_path, title: 'Groups' do
%i.fa.fa-group
%span
Groups
= nav_link(controller: :logs) do
= link_to admin_logs_path do
= link_to admin_logs_path, title: 'Logs' do
%i.fa.fa-file-text
%span
Logs
= nav_link(controller: :broadcast_messages) do
= link_to admin_broadcast_messages_path do
= link_to admin_broadcast_messages_path, title: 'Broadcast Messages' do
%i.fa.fa-bullhorn
%span
Messages
= nav_link(controller: :hooks) do
= link_to admin_hooks_path do
= link_to admin_hooks_path, title: 'Hooks' do
%i.fa.fa-external-link
%span
Hooks
= nav_link(controller: :background_jobs) do
= link_to admin_background_jobs_path do
= link_to admin_background_jobs_path, title: 'Background Jobs' do
%i.fa.fa-cog
%span
Background Jobs
= nav_link(controller: :application_settings) do
= link_to admin_application_settings_path do
= nav_link(controller: :applications) do
= link_to admin_applications_path, title: 'Applications' do
%i.fa.fa-cloud
%span
Applications
= nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do
= link_to admin_application_settings_path, title: 'Settings' do
%i.fa.fa-cogs
%span
Settings
= nav_link(controller: :applications) do
= link_to admin_applications_path do
%i.fa.fa-cloud
%span
Applications

View File

@ -5,24 +5,24 @@
%span
Activity
= nav_link(path: 'dashboard#projects') do
= link_to projects_dashboard_path, class: 'shortcuts-projects' do
= link_to projects_dashboard_path, title: 'Projects', class: 'shortcuts-projects' do
%i.fa.fa-cube
%span
Projects
= nav_link(path: 'dashboard#issues') do
= link_to assigned_issues_dashboard_path, class: 'shortcuts-issues' do
= link_to assigned_issues_dashboard_path, title: 'Issues', class: 'shortcuts-issues' do
%i.fa.fa-exclamation-circle
%span
Issues
%span.count= current_user.assigned_issues.opened.count
= nav_link(path: 'dashboard#merge_requests') do
= link_to assigned_mrs_dashboard_path, class: 'shortcuts-merge_requests' do
= link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'shortcuts-merge_requests' do
%i.fa.fa-tasks
%span
Merge Requests
%span.count= current_user.assigned_merge_requests.opened.count
= nav_link(controller: :help) do
= link_to help_path do
= link_to help_path, title: 'Help' do
%i.fa.fa-question-circle
%span
Help

View File

@ -6,33 +6,33 @@
Activity
- if current_user
= nav_link(controller: [:group, :milestones]) do
= link_to group_milestones_path(@group) do
= link_to group_milestones_path(@group), title: 'Milestones' do
%i.fa.fa-clock-o
%span
Milestones
= nav_link(path: 'groups#issues') do
= link_to issues_group_path(@group) do
= link_to issues_group_path(@group), title: 'Issues' do
%i.fa.fa-exclamation-circle
%span
Issues
- if current_user
%span.count= Issue.opened.of_group(@group).count
= nav_link(path: 'groups#merge_requests') do
= link_to merge_requests_group_path(@group) do
= link_to merge_requests_group_path(@group), title: 'Merge Requests' do
%i.fa.fa-tasks
%span
Merge Requests
- if current_user
%span.count= MergeRequest.opened.of_group(@group).count
= nav_link(path: 'groups#members') do
= link_to members_group_path(@group) do
= link_to members_group_path(@group), title: 'Members' do
%i.fa.fa-users
%span
Members
- if can?(current_user, :manage_group, @group)
= nav_link(html_options: { class: "#{"active" if group_settings_page?} separate-item" }) do
= link_to edit_group_path(@group), class: "tab no-highlight" do
= link_to edit_group_path(@group), title: 'Settings', class: "tab no-highlight" do
%i.fa.fa-cogs
%span
Settings

View File

@ -5,52 +5,51 @@
%span
Profile
= nav_link(controller: :accounts) do
= link_to profile_account_path do
= link_to profile_account_path, title: 'Account' do
%i.fa.fa-gear
%span
Account
= nav_link(path: ['profiles#applications', 'applications#edit', 'applications#show', 'applications#new']) do
= link_to applications_profile_path do
= link_to applications_profile_path, title: 'Applications' do
%i.fa.fa-cloud
%span
Applications
= nav_link(controller: :emails) do
= link_to profile_emails_path do
= link_to profile_emails_path, title: 'Emails' do
%i.fa.fa-envelope-o
%span
Emails
%span.count= current_user.emails.count + 1
- unless current_user.ldap_user?
= nav_link(controller: :passwords) do
= link_to edit_profile_password_path do
= link_to edit_profile_password_path, title: 'Password' do
%i.fa.fa-lock
%span
Password
= nav_link(controller: :notifications) do
= link_to profile_notifications_path do
= link_to profile_notifications_path, title: 'Notifications' do
%i.fa.fa-inbox
%span
Notifications
= nav_link(controller: :keys) do
= link_to profile_keys_path do
= link_to profile_keys_path, title: 'SSH Keys' do
%i.fa.fa-key
%span
SSH Keys
%span.count= current_user.keys.count
= nav_link(path: 'profiles#design') do
= link_to design_profile_path do
= link_to design_profile_path, title: 'Design' do
%i.fa.fa-image
%span
Design
= nav_link(controller: :groups) do
= link_to profile_groups_path do
= link_to profile_groups_path, title: 'Groups' do
%i.fa.fa-group
%span
Groups
= nav_link(path: 'profiles#history') do
= link_to history_profile_path do
= link_to history_profile_path, title: 'History' do
%i.fa.fa-history
%span
History

View File

@ -6,45 +6,44 @@
Project
- if project_nav_tab? :files
= nav_link(controller: %w(tree blob blame edit_tree new_tree)) do
= link_to project_tree_path(@project, @ref || @repository.root_ref), class: 'shortcuts-tree' do
= link_to project_tree_path(@project, @ref || @repository.root_ref), title: 'Files', class: 'shortcuts-tree' do
%i.fa.fa-files-o
%span
Files
- if project_nav_tab? :commits
= nav_link(controller: %w(commit commits compare repositories tags branches)) do
= link_to project_commits_path(@project, @ref || @repository.root_ref), class: 'shortcuts-commits' do
= link_to project_commits_path(@project, @ref || @repository.root_ref), title: 'Commits', class: 'shortcuts-commits' do
%i.fa.fa-history
%span
Commits
- if project_nav_tab? :network
= nav_link(controller: %w(network)) do
= link_to project_network_path(@project, @ref || @repository.root_ref), class: 'shortcuts-network' do
= link_to project_network_path(@project, @ref || @repository.root_ref), title: 'Network', class: 'shortcuts-network' do
%i.fa.fa-code-fork
%span
Network
- if project_nav_tab? :graphs
= nav_link(controller: %w(graphs)) do
= link_to project_graph_path(@project, @ref || @repository.root_ref), class: 'shortcuts-graphs' do
= link_to project_graph_path(@project, @ref || @repository.root_ref), title: 'Graphs', class: 'shortcuts-graphs' do
%i.fa.fa-area-chart
%span
Graphs
- if project_nav_tab? :issues
= nav_link(controller: %w(issues milestones labels)) do
= link_to url_for_project_issues, class: 'shortcuts-issues' do
= link_to url_for_project_issues, title: 'Issues', class: 'shortcuts-issues' do
%i.fa.fa-exclamation-circle
%span
Issues
- if @project.used_default_issues_tracker?
- if @project.default_issues_tracker?
%span.count.issue_counter= @project.issues.opened.count
- if project_nav_tab? :merge_requests
= nav_link(controller: :merge_requests) do
= link_to project_merge_requests_path(@project), class: 'shortcuts-merge_requests' do
= link_to project_merge_requests_path(@project), title: 'Merge Requests', class: 'shortcuts-merge_requests' do
%i.fa.fa-tasks
%span
Merge Requests
@ -52,21 +51,21 @@
- if project_nav_tab? :wiki
= nav_link(controller: :wikis) do
= link_to project_wiki_path(@project, :home), class: 'shortcuts-wiki' do
= link_to project_wiki_path(@project, :home), title: 'Wiki', class: 'shortcuts-wiki' do
%i.fa.fa-book
%span
Wiki
- if project_nav_tab? :snippets
= nav_link(controller: :snippets) do
= link_to project_snippets_path(@project), class: 'shortcuts-snippets' do
= link_to project_snippets_path(@project), title: 'Snippets', class: 'shortcuts-snippets' do
%i.fa.fa-file-text-o
%span
Snippets
- if project_nav_tab? :settings
= nav_link(html_options: {class: "#{project_tab_class} separate-item"}) do
= link_to edit_project_path(@project), class: "stat-tab tab no-highlight" do
= link_to edit_project_path(@project), title: 'Settings', class: "stat-tab tab no-highlight" do
%i.fa.fa-cogs
%span
Settings

View File

@ -2,6 +2,5 @@
%html{ lang: "en"}
= render "layouts/head", title: "Profile"
%body{class: "#{app_theme} #{theme_type} profile", :'data-page' => body_data_page}
= render "layouts/broadcast"
= render "layouts/head_panel", title: "Profile"
= render 'layouts/page', sidebar: 'layouts/nav/profile'

View File

@ -2,7 +2,6 @@
%html{ lang: "en"}
= render "layouts/head", title: @project.name_with_namespace
%body{class: "#{app_theme} #{theme_type} project", :'data-page' => body_data_page, :'data-project-id' => @project.id }
= render "layouts/broadcast"
= render "layouts/head_panel", title: project_title(@project)
= render "layouts/init_auto_complete"
- @project_settings_nav = true

View File

@ -2,7 +2,6 @@
%html{ lang: "en"}
= render "layouts/head", title: project_head_title
%body{class: "#{app_theme} #{theme_type} project", :'data-page' => body_data_page, :'data-project-id' => @project.id }
= render "layouts/broadcast"
= render "layouts/head_panel", title: project_title(@project)
= render "layouts/init_auto_complete"
= render 'layouts/page', sidebar: 'layouts/nav/project'

View File

@ -2,6 +2,5 @@
%html{ lang: "en"}
= render "layouts/head", title: group_head_title
%body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page}
= render "layouts/broadcast"
= render "layouts/public_head_panel", title: "group: #{@group.name}"
= render 'layouts/page', sidebar: 'layouts/nav/group'

View File

@ -2,6 +2,5 @@
%html{ lang: "en"}
= render "layouts/head", title: @project.name_with_namespace
%body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page}
= render "layouts/broadcast"
= render "layouts/public_head_panel", title: project_title(@project)
= render 'layouts/page', sidebar: 'layouts/nav/project'

View File

@ -2,6 +2,5 @@
%html{ lang: "en"}
= render "layouts/head", title: @title
%body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page}
= render "layouts/broadcast"
= render "layouts/public_head_panel", title: @title
= render 'layouts/page'

View File

@ -1,15 +0,0 @@
.file-holder.file
.file-title
%i.icon-file
%span.file_name
%span.monospace.light #{ref}
- if local_assigns[:path]
= ': ' + local_assigns[:path]
.file-content.code
%pre.js-edit-mode-pane#editor
= params[:content] || local_assigns[:blob_data]
- if local_assigns[:path]
.js-edit-mode-pane#preview.hide
.center
%h2
%i.icon-spinner.icon-spin

View File

@ -1,8 +1,9 @@
- empty_repo = @project.empty_repo?
.project-home-panel{:class => ("empty-project" if empty_repo)}
.project-identicon-holder
= project_icon(@project.to_param, alt: '', class: 'avatar')
.project-home-row
.project-home-desc
= project_icon(@project.to_param, alt: '', class: 'avatar s32')
- if @project.description.present?
= escaped_autolink(@project.description)
- if can?(current_user, :admin_project, @project)

View File

@ -1,31 +1,31 @@
%ul.project-settings-nav.sidebar-subnav
= nav_link(path: 'projects#edit') do
= link_to edit_project_path(@project), class: "stat-tab tab " do
= link_to edit_project_path(@project), title: 'Project', class: "stat-tab tab " do
%i.fa.fa-pencil-square-o
%span
Project
= nav_link(controller: [:team_members, :teams]) do
= link_to project_team_index_path(@project), class: "team-tab tab" do
= link_to project_team_index_path(@project), title: 'Members', class: "team-tab tab" do
%i.fa.fa-users
%span
Members
= nav_link(controller: :deploy_keys) do
= link_to project_deploy_keys_path(@project) do
= link_to project_deploy_keys_path(@project), title: 'Deploy Keys' do
%i.fa.fa-key
%span
Deploy Keys
= nav_link(controller: :hooks) do
= link_to project_hooks_path(@project) do
= link_to project_hooks_path(@project), title: 'Web Hooks' do
%i.fa.fa-link
%span
Web Hooks
= nav_link(controller: :services) do
= link_to project_services_path(@project) do
= link_to project_services_path(@project), title: 'Services' do
%i.fa.fa-cogs
%span
Services
= nav_link(controller: :protected_branches) do
= link_to project_protected_branches_path(@project) do
= link_to project_protected_branches_path(@project), title: 'Protected Branches' do
%i.fa.fa-lock
%span
Protected branches

View File

@ -0,0 +1,25 @@
.file-holder.file
.file-title
.editor-ref
%i.fa.fa-code-fork
= ref
%span.editor-file-name
- if @path
%span.monospace
= @path
- if current_action?(:new) || current_action?(:create)
\/
= text_field_tag 'file_name', params[:file_name], placeholder: "File name",
required: true, class: 'form-control new-file-name'
.pull-right
= select_tag :encoding, options_for_select([ "base64", "text" ], "text"), class: 'form-control'
.file-content.code
%pre.js-edit-mode-pane#editor
= params[:content] || local_assigns[:blob_data]
- if local_assigns[:path]
.js-edit-mode-pane#preview.hide
.center
%h2
%i.icon-spinner.icon-spin

View File

@ -0,0 +1,24 @@
.file-editor
%ul.nav.nav-tabs.js-edit-mode
%li.active
= link_to '#editor' do
%i.fa.fa-edit
Edit file
%li
= link_to '#preview', 'data-preview-url' => project_preview_blob_path(@project, @id) do
%i.fa.fa-eye
= editing_preview_title(@blob.name)
= form_tag(project_update_blob_path(@project, @id), method: :put, class: "form-horizontal") do
= render 'projects/blob/editor', ref: @ref, path: @path, blob_data: @blob.data
= render 'shared/commit_message_container', params: params,
placeholder: "Update #{@blob.name}"
= hidden_field_tag 'last_commit', @last_commit
= hidden_field_tag 'content', '', id: "file-content"
= hidden_field_tag 'from_merge_request_id', params[:from_merge_request_id]
= render 'projects/commit_button', ref: @ref,
cancel_path: @after_edit_path
:javascript
blob = new EditBlob(gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}", "#{@blob.language.try(:ace_mode)}")

View File

@ -0,0 +1,12 @@
%h3.page-title New file
.file-editor
= form_tag(project_create_blob_path(@project, @id), method: :post, class: 'form-horizontal form-new-file') do
= render 'projects/blob/editor', ref: @ref
= render 'shared/commit_message_container', params: params,
placeholder: 'Add new file'
= hidden_field_tag 'content', '', id: 'file-content'
= render 'projects/commit_button', ref: @ref,
cancel_path: project_tree_path(@project, @id)
:javascript
blob = new NewBlob(gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}", null)

View File

@ -51,15 +51,6 @@
= f.check_box :issues_enabled
%span.descr Lightweight issue tracking system for this project
- if Project.issues_tracker.values.count > 1
.form-group
= f.label :issues_tracker, "Issues tracker", class: 'control-label'
.col-sm-10= f.select(:issues_tracker, project_issues_trackers(@project.issues_tracker), {}, { disabled: !@project.issues_enabled })
.form-group
= f.label :issues_tracker_id, "Project name or id in issues tracker", class: 'control-label'
.col-sm-10= f.text_field :issues_tracker_id, disabled: !@project.can_have_issues_tracker_id?, class: 'form-control'
.form-group
= f.label :merge_requests_enabled, "Merge Requests", class: 'control-label'
.col-sm-10
@ -89,8 +80,6 @@
.col-sm-10
- if @project.avatar?
= project_icon(@project.to_param, alt: '', class: 'avatar s160')
- else
= project_icon(@project.to_param, alt: '', class: 'avatar s160', only_uploaded: true)
%p.light
- if @project.avatar_in_git
Project avatar in repository: #{ @project.avatar_in_git }

View File

@ -1,57 +0,0 @@
.file-editor
%ul.nav.nav-tabs.js-edit-mode
%li.active
= link_to 'Edit', '#editor'
%li
= link_to editing_preview_title(@blob.name), '#preview', 'data-preview-url' => preview_project_edit_tree_path(@project, @id)
= form_tag(project_edit_tree_path(@project, @id), method: :put, class: "form-horizontal") do
= render 'projects/blob_editor', ref: @ref, path: @path, blob_data: @blob.data
= render 'shared/commit_message_container', params: params,
placeholder: "Update #{@blob.name}"
= hidden_field_tag 'last_commit', @last_commit
= hidden_field_tag 'content', '', id: "file-content"
= hidden_field_tag 'from_merge_request_id', params[:from_merge_request_id]
= render 'projects/commit_button', ref: @ref,
cancel_path: @after_edit_path
:javascript
ace.config.set("modePath", gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}/ace")
ace.config.loadModule("ace/ext/searchbox");
var ace_mode = "#{@blob.language.try(:ace_mode)}";
var editor = ace.edit("editor");
if (ace_mode) {
editor.getSession().setMode('ace/mode/' + ace_mode);
}
disableButtonIfEmptyField("#commit_message", ".js-commit-button");
$(".js-commit-button").click(function(){
$("#file-content").val(editor.getValue());
$(".file-editor form").submit();
});
var editModePanes = $('.js-edit-mode-pane'),
editModeLinks = $('.js-edit-mode a');
editModeLinks.click(function(event) {
event.preventDefault();
var currentLink = $(this),
paneId = currentLink.attr('href'),
currentPane = editModePanes.filter(paneId);
editModeLinks.parent().removeClass('active hover');
currentLink.parent().addClass('active hover');
editModePanes.hide();
if (paneId == '#preview') {
currentPane.fadeIn(200);
$.post(currentLink.data('preview-url'), { content: editor.getValue() }, function(response) {
currentPane.empty().append(response);
})
} else {
currentPane.fadeIn(200);
editor.focus()
}
})

View File

@ -3,6 +3,17 @@
= render "home_panel"
.center.well
%h3
The repository for this project is empty
%h4
You can
= link_to project_new_blob_path(@project, 'master'), class: 'btn btn-new btn-lg' do
add a file
&nbsp;or push it via command line.
%h4
%strong Command line instructions
%div.git-empty
%fieldset
%legend Git global setup

View File

@ -1,5 +1,6 @@
%h3.page-title Fork project
%p.lead Select namespace where to fork this project
%p.lead
Click to fork the project to a user or group
%hr
.fork-namespaces

View File

@ -45,10 +45,17 @@
.automerge_widget.cannot_be_merged.hide
%h4
This request can't be merged with GitLab.
%p
You should do it manually with
%strong
= link_to "command line", "#modal_merge_info", class: "how_to_merge_link", title: "How To Merge", "data-toggle" => "modal"
= link_to "#modal_merge_info", class: "underlined-link how_to_merge_link", title: "How To Merge", "data-toggle" => "modal" do
command line
%p
%button.btn.disabled
%i.fa.fa-warning
Accept Merge Request
&nbsp;
This usually happens when git can not resolve conflicts between branches automatically.
.automerge_widget.unchecked
%p

View File

@ -1,38 +0,0 @@
%h3.page-title New file
%hr
.file-editor
= form_tag(project_new_tree_path(@project, @id), method: :put, class: 'form-horizontal form-new-file') do
.form-group.commit_message-group
= label_tag 'file_name', class: 'control-label' do
File name
.col-sm-10
.input-group
%span.input-group-addon
= @path[-1] == "/" ? @path : @path + "/"
= text_field_tag 'file_name', params[:file_name], placeholder: "sample.rb", required: true, class: 'form-control'
%span.input-group-addon
on
%span= @ref
.form-group.commit_message-group
= label_tag :encoding, class: "control-label" do
Encoding
.col-sm-10
= select_tag :encoding, options_for_select([ "base64", "text" ], "text"), class: 'form-control'
= render 'projects/blob_editor', ref: @ref
= render 'shared/commit_message_container', params: params,
placeholder: 'Add new file'
= hidden_field_tag 'content', '', id: 'file-content'
= render 'projects/commit_button', ref: @ref,
cancel_path: project_tree_path(@project, @id)
:javascript
ace.config.set("modePath", gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}/ace-src-noconflict")
var editor = ace.edit("editor");
disableButtonIfAnyEmptyField($('.form-new-file'), '.form-control', '.btn-create')
$(".js-commit-button").click(function(){
$("#file-content").val(editor.getValue());
$(".file-editor form").submit();
});

View File

@ -17,7 +17,12 @@
%p 2. Set access level for them
.form-group
= f.label :access_level, "Project Access", class: 'control-label'
.col-sm-10= select_tag :access_level, options_for_select(Gitlab::Access.options, @user_project_relation.access_level), class: "project-access-select select2"
.col-sm-10
= select_tag :access_level, options_for_select(Gitlab::Access.options, @user_project_relation.access_level), class: "project-access-select select2"
.help-block
Read more about role permissions
%strong= link_to "here", help_page_path("permissions", "permissions"), class: "vlink"
.form-actions
= f.submit 'Add users', class: "btn btn-create"

View File

@ -10,7 +10,7 @@
= link_to title, '#'
- if current_user && can_push_branch?(@project, @ref)
%li
= link_to project_new_tree_path(@project, @id), title: 'New file', id: 'new-file-link' do
= link_to project_new_blob_path(@project, @id), title: 'New file', id: 'new-file-link' do
%small
%i.fa.fa-plus

View File

@ -0,0 +1,8 @@
%h4 Calendar:
#cal-heatmap.calendar
:javascript
new calendar(
#{@timestamps.to_json},
#{@starting_year},
#{@starting_month}
);

View File

@ -18,6 +18,11 @@
%h4 Groups:
= render 'groups', groups: @groups
%hr
.user-calendar
%h4.center.light
%i.fa.fa-spinner.fa-spin
%hr
%h4
User Activity:
@ -32,3 +37,8 @@
= render 'profile', user: @user
- if @projects.present?
= render 'projects', projects: @projects
:coffeescript
$ ->
$(".user-calendar").load("#{user_calendar_path}")

Some files were not shown because too many files have changed in this diff Show More