Merge branch 'merge_ce' into 'master'
Merge CE into EE See merge request !302
@@ -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)
|
||||
-
|
||||
-
|
||||
@@ -15,28 +15,31 @@ 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
|
||||
- Show success/error messages for test setting button in services
|
||||
-
|
||||
- Fix commits pagination
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
- 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)
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
- Password reset token validity increased from 2 hours to 2 days since it is also send on account creation.
|
||||
-
|
||||
-
|
||||
-
|
||||
@@ -47,16 +50,23 @@ v 7.8.0
|
||||
-
|
||||
-
|
||||
-
|
||||
- Add a new API function that retrieves all issues assigned to a single milestone (Justin Whear and Hannes Rosenögger)
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
- API: Add support for editing an existing project (Mika Mäenpää and Hannes Rosenögger)
|
||||
-
|
||||
-
|
||||
-
|
||||
-
|
||||
- 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
|
||||
- Show setup instructions for GitHub import if disabled
|
||||
- Allow use http for OAuth applications
|
||||
|
||||
v 7.7.0
|
||||
- Import from GitHub.com feature
|
||||
|
||||
@@ -119,8 +119,8 @@ gem "acts-as-taggable-on"
|
||||
# Background jobs
|
||||
gem 'slim'
|
||||
gem 'sinatra', require: nil
|
||||
gem 'sidekiq', '2.17.8'
|
||||
gem 'sidetiq', '0.6.1'
|
||||
gem 'sidetiq', '0.6.3'
|
||||
gem 'sidekiq', '~> 3.3'
|
||||
|
||||
# HTTP requests
|
||||
gem "httparty"
|
||||
@@ -156,6 +156,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"
|
||||
|
||||
@@ -172,7 +175,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"
|
||||
|
||||
@@ -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)
|
||||
@@ -62,8 +63,8 @@ GEM
|
||||
activemodel (>= 3.2.0)
|
||||
activesupport (>= 3.2.0)
|
||||
json (>= 1.7)
|
||||
celluloid (0.15.2)
|
||||
timers (~> 1.1.0)
|
||||
celluloid (0.16.0)
|
||||
timers (~> 4.0.0)
|
||||
charlock_holmes (0.6.9.4)
|
||||
cliver (0.3.2)
|
||||
code_analyzer (0.4.3)
|
||||
@@ -244,6 +245,7 @@ GEM
|
||||
hike (1.2.3)
|
||||
hipchat (1.4.0)
|
||||
httparty
|
||||
hitimes (1.2.2)
|
||||
html-pipeline (1.11.0)
|
||||
activesupport (>= 2)
|
||||
nokogiri (~> 1.4)
|
||||
@@ -259,7 +261,7 @@ GEM
|
||||
httpauth (0.2.1)
|
||||
httpclient (2.5.3.3)
|
||||
i18n (0.6.11)
|
||||
ice_cube (0.12.1)
|
||||
ice_cube (0.11.1)
|
||||
ice_nine (0.10.0)
|
||||
jasmine (2.0.2)
|
||||
jasmine-core (~> 2.0.0)
|
||||
@@ -490,22 +492,22 @@ 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)
|
||||
shoulda-matchers (2.1.0)
|
||||
activesupport (>= 3.0.0)
|
||||
sidekiq (2.17.8)
|
||||
celluloid (= 0.15.2)
|
||||
connection_pool (~> 2.0)
|
||||
sidekiq (3.3.0)
|
||||
celluloid (>= 0.16.0)
|
||||
connection_pool (>= 2.0.0)
|
||||
json
|
||||
redis (~> 3.1)
|
||||
redis-namespace (~> 1.3)
|
||||
sidetiq (0.6.1)
|
||||
redis (>= 3.0.6)
|
||||
redis-namespace (>= 1.3.1)
|
||||
sidetiq (0.6.3)
|
||||
celluloid (>= 0.14.1)
|
||||
ice_cube (~> 0.12.0)
|
||||
sidekiq (>= 2.16.0)
|
||||
ice_cube (= 0.11.1)
|
||||
sidekiq (>= 3.0.0)
|
||||
simple_oauth (0.1.9)
|
||||
simplecov (0.9.0)
|
||||
docile (~> 1.1.0)
|
||||
@@ -560,7 +562,8 @@ GEM
|
||||
thor (0.19.1)
|
||||
thread_safe (0.3.4)
|
||||
tilt (1.4.1)
|
||||
timers (1.1.0)
|
||||
timers (4.0.1)
|
||||
hitimes
|
||||
timfel-krb5-auth (0.8)
|
||||
tinder (1.9.3)
|
||||
eventmachine (~> 1.0)
|
||||
@@ -630,6 +633,7 @@ DEPENDENCIES
|
||||
binding_of_caller
|
||||
bootstrap-sass (~> 3.0)
|
||||
browser
|
||||
cal-heatmap-rails (~> 0.0.1)
|
||||
capybara (~> 2.2.1)
|
||||
carrierwave
|
||||
coffee-rails
|
||||
@@ -719,11 +723,11 @@ 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 (= 2.17.8)
|
||||
sidetiq (= 0.6.1)
|
||||
sidekiq (~> 3.3)
|
||||
sidetiq (= 0.6.3)
|
||||
simplecov
|
||||
sinatra
|
||||
six
|
||||
|
||||
|
Before Width: | Height: | Size: 1.9 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 210 B After Width: | Height: | Size: 90 B |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 167 B |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 396 B After Width: | Height: | Size: 367 B |
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 691 B After Width: | Height: | Size: 418 B |
|
Before Width: | Height: | Size: 1019 B After Width: | Height: | Size: 726 B |
|
Before Width: | Height: | Size: 331 B After Width: | Height: | Size: 222 B |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 6.5 KiB After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.2 KiB |
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 260 B After Width: | Height: | Size: 197 B |
|
Before Width: | Height: | Size: 704 B After Width: | Height: | Size: 621 B |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 942 B |
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 231 B |
|
Before Width: | Height: | Size: 50 B After Width: | Height: | Size: 49 B |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 3.7 KiB |
@@ -39,6 +39,7 @@
|
||||
#= require shortcuts_dashboard_navigation
|
||||
#= require shortcuts_issueable
|
||||
#= require shortcuts_network
|
||||
#= require cal-heatmap
|
||||
#= require_tree .
|
||||
|
||||
window.slugify = (text) ->
|
||||
@@ -74,24 +75,18 @@ window.disableButtonIfEmptyField = (field_selector, button_selector) ->
|
||||
# Disable button if any input field with given selector is empty
|
||||
window.disableButtonIfAnyEmptyField = (form, form_selector, button_selector) ->
|
||||
closest_submit = form.find(button_selector)
|
||||
empty = false
|
||||
form.find('input').filter(form_selector).each ->
|
||||
empty = true if rstrip($(this).val()) is ""
|
||||
|
||||
if empty
|
||||
closest_submit.disable()
|
||||
else
|
||||
closest_submit.enable()
|
||||
|
||||
form.keyup ->
|
||||
empty = false
|
||||
updateButtons = ->
|
||||
filled = true
|
||||
form.find('input').filter(form_selector).each ->
|
||||
empty = true if rstrip($(this).val()) is ""
|
||||
filled = rstrip($(this).val()) != "" || !$(this).attr('required')
|
||||
|
||||
if empty
|
||||
closest_submit.disable()
|
||||
else
|
||||
if filled
|
||||
closest_submit.enable()
|
||||
else
|
||||
closest_submit.disable()
|
||||
|
||||
updateButtons()
|
||||
form.keyup(updateButtons)
|
||||
|
||||
window.sanitize = (str) ->
|
||||
return str.replace(/<(?:.|\n)*?>/gm, '')
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -100,6 +100,7 @@ class Dispatcher
|
||||
new Profile()
|
||||
when 'projects'
|
||||
new Project()
|
||||
new ProjectAvatar()
|
||||
switch path[1]
|
||||
when 'edit'
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
class @ProjectAvatar
|
||||
constructor: ->
|
||||
$('.js-choose-project-avatar-button').bind 'click', ->
|
||||
form = $(this).closest('form')
|
||||
form.find('.js-project-avatar-input').click()
|
||||
$('.js-project-avatar-input').bind 'change', ->
|
||||
form = $(this).closest('form')
|
||||
filename = $(this).val().replace(/^.*[\\\/]/, '')
|
||||
form.find('.js-avatar-filename').text(filename)
|
||||
@@ -10,20 +10,6 @@ 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')
|
||||
|
||||
$("#project_merge_requests_enabled").change ->
|
||||
checked = $(this).prop("checked")
|
||||
$("#project_merge_requests_template").prop "disabled", not checked
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
*= require select2
|
||||
*= require_self
|
||||
*= require dropzone/basic
|
||||
*= require cal-heatmap
|
||||
*/
|
||||
|
||||
@import "main/*";
|
||||
|
||||
@@ -2,15 +2,17 @@
|
||||
float: left;
|
||||
margin-right: 12px;
|
||||
width: 40px;
|
||||
padding: 1px;
|
||||
@include border-radius(4px);
|
||||
height: 40px;
|
||||
padding: 0;
|
||||
@include border-radius($avatar_radius);
|
||||
|
||||
&.avatar-inline {
|
||||
float: none;
|
||||
margin-left: 3px;
|
||||
margin-left: 4px;
|
||||
margin-bottom: 2px;
|
||||
|
||||
&.s16 { margin-right: 2px; }
|
||||
&.s24 { margin-right: 2px; }
|
||||
&.s16 { margin-right: 4px; }
|
||||
&.s24 { margin-right: 4px; }
|
||||
}
|
||||
|
||||
&.s16 { width: 16px; height: 16px; margin-right: 6px; }
|
||||
@@ -21,3 +23,16 @@
|
||||
&.s90 { width: 90px; height: 90px; margin-right: 15px; }
|
||||
&.s160 { width: 160px; height: 160px; margin-right: 20px; }
|
||||
}
|
||||
|
||||
.identicon {
|
||||
text-align: center;
|
||||
vertical-align: top;
|
||||
|
||||
&.s16 { font-size: 12px; line-height: 1.33; }
|
||||
&.s24 { font-size: 14px; line-height: 1.8; }
|
||||
&.s26 { font-size: 20px; line-height: 1.33; }
|
||||
&.s32 { font-size: 24px; line-height: 1.33; }
|
||||
&.s60 { font-size: 45px; line-height: 1.33; }
|
||||
&.s90 { font-size: 68px; line-height: 1.33; }
|
||||
&.s160 { font-size: 120px; line-height: 1.33; }
|
||||
}
|
||||
|
||||
@@ -173,6 +173,11 @@
|
||||
margin-right: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
&.btn-lg {
|
||||
font-size: 15px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-block {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
background: #fff;
|
||||
color: #737881;
|
||||
float: left;
|
||||
@include border-radius(40px);
|
||||
@include border-radius($avatar_radius);
|
||||
@include box-shadow(0 0 0 3px #EEE);
|
||||
overflow: hidden;
|
||||
|
||||
@@ -58,6 +58,10 @@
|
||||
padding: 10px 15px;
|
||||
margin-left: 60px;
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
display: block;
|
||||
|
||||
@@ -128,3 +128,7 @@ a:focus {
|
||||
textarea.js-gfm-input {
|
||||
font-family: $monospace_font;
|
||||
}
|
||||
|
||||
.strikethrough {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* General Colors
|
||||
*/
|
||||
$style_color: #474D57;
|
||||
$hover: #FFECDB;
|
||||
$hover: #FFF3EB;
|
||||
$box_bg: #F9F9F9;
|
||||
|
||||
/*
|
||||
@@ -57,3 +57,7 @@ $list-font-size: 15px;
|
||||
* Sidebar navigation width
|
||||
*/
|
||||
$sidebar_width: 230px;
|
||||
|
||||
$avatar_radius: 50%;
|
||||
$code_font_size: 13px;
|
||||
$code_line_height: 1.5;
|
||||
|
||||
@@ -101,7 +101,6 @@
|
||||
|
||||
.commit-title {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.commit-description {
|
||||
|
||||
@@ -75,6 +75,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.project-avatar {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.project-description {
|
||||
overflow: hidden;
|
||||
@@ -92,8 +95,24 @@
|
||||
}
|
||||
}
|
||||
|
||||
.dash-project-avatar {
|
||||
float: left;
|
||||
}
|
||||
.dash-project-access-icon {
|
||||
float: left;
|
||||
margin-right: 3px;
|
||||
margin-right: 5px;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
.dash-new-project {
|
||||
background: $bg_success;
|
||||
border: 1px solid $border_success;
|
||||
|
||||
a {
|
||||
color: #FFF;
|
||||
}
|
||||
}
|
||||
|
||||
.dash-list .str-truncated {
|
||||
max-width: 72%;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,9 +138,10 @@ header {
|
||||
top: -1px;
|
||||
padding-right: 0px !important;
|
||||
img {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
@include border-radius(4px);
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
margin: -15px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -163,8 +163,9 @@ form.edit-issue {
|
||||
}
|
||||
}
|
||||
|
||||
.issue-title {
|
||||
h3.issue-title {
|
||||
margin-top: 0;
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.context .select2-container {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -52,6 +52,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.note_text {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -102,12 +102,3 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.profile-groups-avatars {
|
||||
margin: 0 5px 10px 0;
|
||||
|
||||
img {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -49,7 +49,7 @@ class ApplicationController < ActionController::Base
|
||||
end
|
||||
|
||||
def authenticate_user!(*args)
|
||||
# If user is not signe-in and tries to access root_path - redirect him to landing page
|
||||
# If user is not signed-in and tries to access root_path - redirect him to landing page
|
||||
if current_application_settings.home_page_url.present?
|
||||
if current_user.nil? && controller_name == 'dashboard' && action_name == 'show'
|
||||
redirect_to current_application_settings.home_page_url and return
|
||||
@@ -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
|
||||
|
||||
@@ -63,11 +63,11 @@ class GithubImportsController < ApplicationController
|
||||
|
||||
def github_auth
|
||||
if current_user.github_access_token.blank?
|
||||
go_to_gihub_for_permissions
|
||||
go_to_github_for_permissions
|
||||
end
|
||||
end
|
||||
|
||||
def go_to_gihub_for_permissions
|
||||
def go_to_github_for_permissions
|
||||
redirect_to client.auth_code.authorize_url({
|
||||
redirect_uri: callback_github_import_url,
|
||||
scope: "repo, user, user:email"
|
||||
@@ -75,6 +75,6 @@ class GithubImportsController < ApplicationController
|
||||
end
|
||||
|
||||
def github_unauthorized
|
||||
go_to_gihub_for_permissions
|
||||
go_to_github_for_permissions
|
||||
end
|
||||
end
|
||||
|
||||
@@ -5,12 +5,12 @@ class PasswordsController < Devise::PasswordsController
|
||||
resource_found = resource_class.find_by_email(email)
|
||||
if resource_found && resource_found.ldap_user?
|
||||
flash[:alert] = "Cannot reset password for LDAP user."
|
||||
respond_with({}, :location => after_sending_reset_password_instructions_path_for(resource_name)) and return
|
||||
respond_with({}, location: after_sending_reset_password_instructions_path_for(resource_name)) and return
|
||||
end
|
||||
|
||||
self.resource = resource_class.send_reset_password_instructions(resource_params)
|
||||
if successfully_sent?(resource)
|
||||
respond_with({}, :location => after_sending_reset_password_instructions_path_for(resource_name))
|
||||
respond_with({}, location: after_sending_reset_password_instructions_path_for(resource_name))
|
||||
else
|
||||
respond_with(resource)
|
||||
end
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
class Projects::AvatarsController < Projects::ApplicationController
|
||||
layout 'project'
|
||||
|
||||
before_filter :project
|
||||
|
||||
def show
|
||||
@blob = @project.repository.blob_at_branch('master', @project.avatar_in_git)
|
||||
if @blob
|
||||
headers['X-Content-Type-Options'] = 'nosniff'
|
||||
send_data(
|
||||
@blob.data,
|
||||
type: @blob.mime_type,
|
||||
disposition: 'inline',
|
||||
filename: @blob.name
|
||||
)
|
||||
else
|
||||
not_found!
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
@project.remove_avatar!
|
||||
|
||||
@project.save
|
||||
@project.reset_events_cache
|
||||
|
||||
redirect_to edit_project_path(@project)
|
||||
end
|
||||
end
|
||||
@@ -1,7 +0,0 @@
|
||||
class Projects::BaseTreeController < Projects::ApplicationController
|
||||
include ExtractsPath
|
||||
|
||||
before_filter :authorize_download_code!
|
||||
before_filter :require_non_empty_project
|
||||
end
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -24,7 +24,7 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController
|
||||
)
|
||||
|
||||
respond_to do |format|
|
||||
format.json { render :json => protected_branch, status: :ok }
|
||||
format.json { render json: protected_branch, status: :ok }
|
||||
end
|
||||
else
|
||||
respond_to do |format|
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -41,9 +41,9 @@ class Projects::RefsController < Projects::ApplicationController
|
||||
@path = params[:path]
|
||||
|
||||
contents = []
|
||||
contents += tree.trees
|
||||
contents += tree.blobs
|
||||
contents += tree.submodules
|
||||
contents.push(*tree.trees)
|
||||
contents.push(*tree.blobs)
|
||||
contents.push(*tree.submodules)
|
||||
|
||||
@logs = contents[@offset, @limit].to_a.map do |content|
|
||||
file = @path ? File.join(@path, content.name) : content.name
|
||||
|
||||
@@ -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
|
||||
@@ -25,9 +26,13 @@ class Projects::ServicesController < Projects::ApplicationController
|
||||
|
||||
def test
|
||||
data = Gitlab::PushDataBuilder.build_sample(project, current_user)
|
||||
@service.execute(data)
|
||||
if @service.execute(data)
|
||||
message = { notice: 'We sent a request to the provided URL' }
|
||||
else
|
||||
message = { alert: 'We tried to send a request to the provided URL but error occured' }
|
||||
end
|
||||
|
||||
redirect_to :back
|
||||
redirect_to :back, message
|
||||
end
|
||||
|
||||
private
|
||||
@@ -41,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -14,7 +14,7 @@ class ProjectsController < ApplicationController
|
||||
end
|
||||
|
||||
def edit
|
||||
render 'edit', layout: "project_settings"
|
||||
render 'edit', layout: 'project_settings'
|
||||
end
|
||||
|
||||
def create
|
||||
@@ -36,7 +36,7 @@ class ProjectsController < ApplicationController
|
||||
format.html { redirect_to edit_project_path(@project), notice: 'Project was successfully updated.' }
|
||||
format.js
|
||||
else
|
||||
format.html { render "edit", layout: "project_settings" }
|
||||
format.html { render 'edit', layout: 'project_settings' }
|
||||
format.js
|
||||
end
|
||||
end
|
||||
@@ -66,17 +66,17 @@ class ProjectsController < ApplicationController
|
||||
format.html do
|
||||
if @project.repository_exists?
|
||||
if @project.empty_repo?
|
||||
render "projects/empty", layout: user_layout
|
||||
render 'projects/empty', layout: user_layout
|
||||
else
|
||||
@last_push = current_user.recent_push(@project.id) if current_user
|
||||
render :show, layout: user_layout
|
||||
end
|
||||
else
|
||||
render "projects/no_repo", layout: user_layout
|
||||
render 'projects/no_repo', layout: user_layout
|
||||
end
|
||||
end
|
||||
|
||||
format.json { pager_json("events/_events", @events.count) }
|
||||
format.json { pager_json('events/_events', @events.count) }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -87,9 +87,9 @@ class ProjectsController < ApplicationController
|
||||
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
flash[:alert] = "Project deleted."
|
||||
flash[:alert] = 'Project deleted.'
|
||||
|
||||
if request.referer.include?("/admin")
|
||||
if request.referer.include?('/admin')
|
||||
redirect_to admin_projects_path
|
||||
else
|
||||
redirect_to projects_dashboard_path
|
||||
@@ -101,16 +101,18 @@ class ProjectsController < ApplicationController
|
||||
def autocomplete_sources
|
||||
note_type = params['type']
|
||||
note_id = params['type_id']
|
||||
autocomplete = ::Projects::AutocompleteService.new(@project)
|
||||
participants = ::Projects::ParticipantsService.new(@project).execute(note_type, note_id)
|
||||
|
||||
@suggestions = {
|
||||
emojis: Emoji.names.map { |e| { name: e, path: view_context.image_url("emoji/#{e}.png") } },
|
||||
issues: @project.issues.select([:iid, :title, :description]),
|
||||
mergerequests: @project.merge_requests.select([:iid, :title, :description]),
|
||||
emojis: autocomplete_emojis,
|
||||
issues: autocomplete.issues,
|
||||
mergerequests: autocomplete.merge_requests,
|
||||
members: participants
|
||||
}
|
||||
|
||||
respond_to do |format|
|
||||
format.json { render :json => @suggestions }
|
||||
format.json { render json: @suggestions }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -139,7 +141,7 @@ class ProjectsController < ApplicationController
|
||||
if link_to_image
|
||||
format.json { render json: { link: link_to_image } }
|
||||
else
|
||||
format.json { render json: "Invalid file.", status: :unprocessable_entity }
|
||||
format.json { render json: 'Invalid file.', status: :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -170,15 +172,26 @@ class ProjectsController < ApplicationController
|
||||
end
|
||||
|
||||
def user_layout
|
||||
current_user ? "projects" : "public_projects"
|
||||
current_user ? 'projects' : 'public_projects'
|
||||
end
|
||||
|
||||
def project_params
|
||||
params.require(:project).permit(
|
||||
:name, :path, :description, :issues_tracker, :tag_list,
|
||||
:issues_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id, :default_branch,
|
||||
:wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :merge_requests_template,
|
||||
:merge_requests_rebase_enabled
|
||||
:wiki_enabled, :merge_requests_template, :visibility_level, :merge_requests_rebase_enabled,
|
||||
:import_url, :last_activity_at, :namespace_id, :avatar
|
||||
)
|
||||
end
|
||||
|
||||
def autocomplete_emojis
|
||||
Rails.cache.fetch("autocomplete-emoji-#{Emoji::VERSION}") do
|
||||
Emoji.names.map do |e|
|
||||
{
|
||||
name: e,
|
||||
path: view_context.image_url("emoji/#{e}.png")
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
class UsersController < ApplicationController
|
||||
skip_before_filter :authenticate_user!, only: [:show]
|
||||
skip_before_filter :authenticate_user!, only: [:show, :activities]
|
||||
layout :determine_layout
|
||||
|
||||
def show
|
||||
@@ -10,7 +10,8 @@ class UsersController < ApplicationController
|
||||
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)
|
||||
@@ -24,6 +25,13 @@ class UsersController < ApplicationController
|
||||
|
||||
@title = @user.name
|
||||
|
||||
# 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
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.atom { render layout: false }
|
||||
|
||||
@@ -50,6 +50,38 @@ module ApplicationHelper
|
||||
args.any? { |v| v.to_s.downcase == action_name }
|
||||
end
|
||||
|
||||
def project_icon(project_id, options = {})
|
||||
project = Project.find_with_namespace(project_id)
|
||||
if project.avatar.present?
|
||||
image_tag project.avatar.url, options
|
||||
elsif project.avatar_in_git
|
||||
image_tag project_avatar_path(project), options
|
||||
else # generated icon
|
||||
project_identicon(project, options)
|
||||
end
|
||||
end
|
||||
|
||||
def project_identicon(project, options = {})
|
||||
allowed_colors = {
|
||||
red: 'FFEBEE',
|
||||
purple: 'F3E5F5',
|
||||
indigo: 'E8EAF6',
|
||||
blue: 'E3F2FD',
|
||||
teal: 'E0F2F1',
|
||||
orange: 'FBE9E7',
|
||||
gray: 'EEEEEE'
|
||||
}
|
||||
|
||||
options[:class] ||= ''
|
||||
options[:class] << ' identicon'
|
||||
bg_key = project.id % 7
|
||||
|
||||
content_tag(:div, class: options[:class],
|
||||
style: "background-color: ##{ allowed_colors.values[bg_key] }; color: #555") do
|
||||
project.name[0, 1].upcase
|
||||
end
|
||||
end
|
||||
|
||||
def group_icon(group_path)
|
||||
group = Group.find_by(path: group_path)
|
||||
if group && group.avatar.present?
|
||||
@@ -82,24 +114,24 @@ module ApplicationHelper
|
||||
if project.repo_exists?
|
||||
time_ago_with_tooltip(project.repository.commit.committed_date)
|
||||
else
|
||||
"Never"
|
||||
'Never'
|
||||
end
|
||||
rescue
|
||||
"Never"
|
||||
'Never'
|
||||
end
|
||||
|
||||
def grouped_options_refs
|
||||
repository = @project.repository
|
||||
|
||||
options = [
|
||||
["Branches", repository.branch_names],
|
||||
["Tags", VersionSorter.rsort(repository.tag_names)]
|
||||
['Branches', repository.branch_names],
|
||||
['Tags', VersionSorter.rsort(repository.tag_names)]
|
||||
]
|
||||
|
||||
# If reference is commit id - we should add it to branch/tag selectbox
|
||||
if(@ref && !options.flatten.include?(@ref) &&
|
||||
@ref =~ /^[0-9a-zA-Z]{6,52}$/)
|
||||
options << ["Commit", [@ref]]
|
||||
options << ['Commit', [@ref]]
|
||||
end
|
||||
|
||||
grouped_options_for_select(options, @ref || @project.default_branch)
|
||||
@@ -161,7 +193,7 @@ module ApplicationHelper
|
||||
path = controller.controller_path.split('/')
|
||||
namespace = path.first if path.second
|
||||
|
||||
[namespace, controller.controller_name, controller.action_name].compact.join(":")
|
||||
[namespace, controller.controller_name, controller.action_name].compact.join(':')
|
||||
end
|
||||
|
||||
# shortcut for gitlab config
|
||||
@@ -176,13 +208,13 @@ module ApplicationHelper
|
||||
|
||||
def search_placeholder
|
||||
if @project && @project.persisted?
|
||||
"Search in this project"
|
||||
'Search in this project'
|
||||
elsif @snippet || @snippets || @show_snippets
|
||||
'Search snippets'
|
||||
elsif @group && @group.persisted?
|
||||
"Search in this group"
|
||||
'Search in this group'
|
||||
else
|
||||
"Search"
|
||||
'Search'
|
||||
end
|
||||
end
|
||||
|
||||
@@ -193,7 +225,7 @@ module ApplicationHelper
|
||||
def time_ago_with_tooltip(date, placement = 'top', html_class = 'time_ago')
|
||||
capture_haml do
|
||||
haml_tag :time, date.to_s,
|
||||
class: html_class, datetime: date.getutc.iso8601, title: date.stamp("Aug 21, 2011 9:23pm"),
|
||||
class: html_class, datetime: date.getutc.iso8601, title: date.stamp('Aug 21, 2011 9:23pm'),
|
||||
data: { toggle: 'tooltip', placement: placement }
|
||||
|
||||
haml_tag :script, "$('." + html_class + "').timeago().tooltip()"
|
||||
@@ -216,8 +248,8 @@ module ApplicationHelper
|
||||
end
|
||||
|
||||
def spinner(text = nil, visible = false)
|
||||
css_class = "loading"
|
||||
css_class << " hide" unless visible
|
||||
css_class = 'loading'
|
||||
css_class << ' hide' unless visible
|
||||
|
||||
content_tag :div, class: css_class do
|
||||
content_tag(:i, nil, class: 'fa fa-spinner fa-spin') + text
|
||||
@@ -234,17 +266,17 @@ module ApplicationHelper
|
||||
absolute_uri = nil
|
||||
end
|
||||
|
||||
# Add "nofollow" only to external links
|
||||
# Add 'nofollow' only to external links
|
||||
if host && host != Gitlab.config.gitlab.host && absolute_uri
|
||||
if html_options
|
||||
if html_options[:rel]
|
||||
html_options[:rel] << " nofollow"
|
||||
html_options[:rel] << ' nofollow'
|
||||
else
|
||||
html_options.merge!(rel: "nofollow")
|
||||
html_options.merge!(rel: 'nofollow')
|
||||
end
|
||||
else
|
||||
html_options = Hash.new
|
||||
html_options[:rel] = "nofollow"
|
||||
html_options[:rel] = 'nofollow'
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -44,7 +44,7 @@ module CommitsHelper
|
||||
parts = @path.split('/')
|
||||
|
||||
parts.each_with_index do |part, i|
|
||||
crumbs += content_tag(:li) do
|
||||
crumbs << content_tag(:li) do
|
||||
# The text is just the individual part, but the link needs all the parts before it
|
||||
link_to part, project_commits_path(@project, tree_join(@ref, parts[0..i].join('/')))
|
||||
end
|
||||
@@ -62,13 +62,27 @@ module CommitsHelper
|
||||
|
||||
# Returns the sorted alphabetically links to branches, separated by a comma
|
||||
def commit_branches_links(project, branches)
|
||||
branches.sort.map { |branch| link_to(branch, project_tree_path(project, branch)) }.join(", ").html_safe
|
||||
branches.sort.map do |branch|
|
||||
link_to(project_tree_path(project, branch)) do
|
||||
content_tag :span, class: 'label label-gray' do
|
||||
content_tag(:i, nil, class: 'fa fa-code-fork') + ' ' +
|
||||
branch
|
||||
end
|
||||
end
|
||||
end.join(" ").html_safe
|
||||
end
|
||||
|
||||
# Returns the sorted links to tags, separated by a comma
|
||||
def commit_tags_links(project, tags)
|
||||
sorted = VersionSorter.rsort(tags)
|
||||
sorted.map { |tag| link_to(tag, project_commits_path(project, project.repository.find_tag(tag).name)) }.join(", ").html_safe
|
||||
sorted.map do |tag|
|
||||
link_to(project_commits_path(project, project.repository.find_tag(tag).name)) do
|
||||
content_tag :span, class: 'label label-gray' do
|
||||
content_tag(:i, nil, class: 'fa fa-tag') + ' ' +
|
||||
tag
|
||||
end
|
||||
end
|
||||
end.join(" ").html_safe
|
||||
end
|
||||
|
||||
def link_to_browse_code(project, commit)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
module GraphHelper
|
||||
def get_refs(repo, commit)
|
||||
refs = ""
|
||||
refs += commit.ref_names(repo).join(" ")
|
||||
refs << commit.ref_names(repo).join(' ')
|
||||
|
||||
# append note count
|
||||
refs += "[#{@graph.notes[commit.id]}]" if @graph.notes[commit.id] > 0
|
||||
refs << "[#{@graph.notes[commit.id]}]" if @graph.notes[commit.id] > 0
|
||||
|
||||
refs
|
||||
end
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -72,21 +72,9 @@ 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
|
||||
cls << ' disabled' unless signed_in
|
||||
|
||||
toggle_html = content_tag('span', class: 'toggle') do
|
||||
toggle_text = if starred
|
||||
@@ -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
|
||||
|
||||
def membership_locked?
|
||||
|
||||
@@ -6,7 +6,7 @@ module TagsHelper
|
||||
def tag_list(project)
|
||||
html = ''
|
||||
project.tag_list.each do |tag|
|
||||
html += link_to tag, tag_path(tag)
|
||||
html << link_to(tag, tag_path(tag))
|
||||
end
|
||||
|
||||
html.html_safe
|
||||
|
||||
@@ -10,13 +10,16 @@ module TreeHelper
|
||||
tree = ""
|
||||
|
||||
# Render folders if we have any
|
||||
tree += render partial: 'projects/tree/tree_item', collection: folders, locals: {type: 'folder'} if folders.present?
|
||||
tree << render(partial: 'projects/tree/tree_item', collection: folders,
|
||||
locals: { type: 'folder' }) if folders.present?
|
||||
|
||||
# Render files if we have any
|
||||
tree += render partial: 'projects/tree/blob_item', collection: files, locals: {type: 'file'} if files.present?
|
||||
tree << render(partial: 'projects/tree/blob_item', collection: files,
|
||||
locals: { type: 'file' }) if files.present?
|
||||
|
||||
# Render submodules if we have any
|
||||
tree += render partial: 'projects/tree/submodule_item', collection: submodules if submodules.present?
|
||||
tree << render(partial: 'projects/tree/submodule_item',
|
||||
collection: submodules) if submodules.present?
|
||||
|
||||
tree.html_safe
|
||||
end
|
||||
@@ -61,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 = ""
|
||||
@@ -109,7 +86,7 @@ module TreeHelper
|
||||
tree_join(@ref, file)
|
||||
end
|
||||
|
||||
# returns the relative path of the first subdir that doesn't have only one directory descendand
|
||||
# returns the relative path of the first subdir that doesn't have only one directory descendant
|
||||
def flatten_tree(tree)
|
||||
subtree = Gitlab::Git::Tree.where(@repository, @commit.id, tree.path)
|
||||
if subtree.count == 1 && subtree.first.dir?
|
||||
@@ -118,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
|
||||
|
||||
@@ -56,7 +56,7 @@ module Emails
|
||||
end
|
||||
end
|
||||
|
||||
# Over rides default behavour to show source/target
|
||||
# Over rides default behaviour to show source/target
|
||||
# Formats arguments into a String suitable for use as an email subject
|
||||
#
|
||||
# extra - Extra Strings to be inserted into the subject
|
||||
|
||||
@@ -27,8 +27,8 @@ class Notify < ActionMailer::Base
|
||||
delay_for(2.seconds)
|
||||
end
|
||||
|
||||
def test_email(recepient_email, subject, body)
|
||||
mail(to: recepient_email,
|
||||
def test_email(recipient_email, subject, body)
|
||||
mail(to: recipient_email,
|
||||
subject: subject,
|
||||
body: body.html_safe,
|
||||
content_type: 'text/html'
|
||||
|
||||
@@ -73,28 +73,28 @@ class Ability
|
||||
|
||||
# Rules based on role in project
|
||||
if team.master?(user)
|
||||
rules += project_master_rules
|
||||
rules.push(*project_master_rules)
|
||||
|
||||
elsif team.developer?(user)
|
||||
rules += project_dev_rules
|
||||
rules.push(*project_dev_rules)
|
||||
|
||||
elsif team.reporter?(user)
|
||||
rules += project_report_rules
|
||||
rules.push(*project_report_rules)
|
||||
|
||||
elsif team.guest?(user)
|
||||
rules += project_guest_rules
|
||||
rules.push(*project_guest_rules)
|
||||
end
|
||||
|
||||
if project.public? || project.internal?
|
||||
rules += public_project_rules
|
||||
rules.push(*public_project_rules)
|
||||
end
|
||||
|
||||
if project.owner == user || user.admin?
|
||||
rules += project_admin_rules
|
||||
rules.push(*project_admin_rules)
|
||||
end
|
||||
|
||||
if project.group && project.group.has_owner?(user)
|
||||
rules += project_admin_rules
|
||||
rules.push(*project_admin_rules)
|
||||
end
|
||||
|
||||
if project.archived?
|
||||
@@ -193,17 +193,17 @@ class Ability
|
||||
|
||||
# Only group masters and group owners can create new projects in group
|
||||
if group.has_master?(user) || group.has_owner?(user) || user.admin?
|
||||
rules += [
|
||||
rules.push(*[
|
||||
:create_projects,
|
||||
]
|
||||
])
|
||||
end
|
||||
|
||||
# Only group owner and administrators can manage group
|
||||
if group.has_owner?(user) || user.admin?
|
||||
rules += [
|
||||
rules.push(*[
|
||||
:manage_group,
|
||||
:manage_namespace
|
||||
]
|
||||
])
|
||||
end
|
||||
|
||||
rules.flatten
|
||||
@@ -214,10 +214,10 @@ class Ability
|
||||
|
||||
# Only namespace owner and administrators can manage it
|
||||
if namespace.owner == user || user.admin?
|
||||
rules += [
|
||||
rules.push(*[
|
||||
:create_projects,
|
||||
:manage_namespace
|
||||
]
|
||||
])
|
||||
end
|
||||
|
||||
rules.flatten
|
||||
|
||||
@@ -1,3 +1,18 @@
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: application_settings
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# default_projects_limit :integer
|
||||
# signup_enabled :boolean
|
||||
# signin_enabled :boolean
|
||||
# gravatar_enabled :boolean
|
||||
# sign_in_text :text
|
||||
# created_at :datetime
|
||||
# updated_at :datetime
|
||||
# home_page_url :string(255)
|
||||
#
|
||||
|
||||
class ApplicationSetting < ActiveRecord::Base
|
||||
validates :home_page_url, allow_blank: true,
|
||||
format: { with: URI::regexp(%w(http https)), message: "should be a valid url" },
|
||||
|
||||
@@ -88,7 +88,7 @@ module Issuable
|
||||
|
||||
# Return the number of -1 comments (downvotes)
|
||||
def downvotes
|
||||
notes.select(&:downvote?).size
|
||||
filter_superceded_votes(notes.select(&:downvote?), notes).size
|
||||
end
|
||||
|
||||
def downvotes_in_percent
|
||||
@@ -101,7 +101,7 @@ module Issuable
|
||||
|
||||
# Return the number of +1 comments (upvotes)
|
||||
def upvotes
|
||||
notes.select(&:upvote?).size
|
||||
filter_superceded_votes(notes.select(&:upvote?), notes).size
|
||||
end
|
||||
|
||||
def upvotes_in_percent
|
||||
@@ -124,10 +124,12 @@ module Issuable
|
||||
users << assignee if is_assigned?
|
||||
mentions = []
|
||||
mentions << self.mentioned_users
|
||||
|
||||
notes.each do |note|
|
||||
users << note.author
|
||||
mentions << note.mentioned_users
|
||||
end
|
||||
|
||||
users.concat(mentions.reduce([], :|)).uniq
|
||||
end
|
||||
|
||||
@@ -149,9 +151,23 @@ module Issuable
|
||||
|
||||
def add_labels_by_names(label_names)
|
||||
label_names.each do |label_name|
|
||||
label = project.labels.create_with(
|
||||
color: Label::DEFAULT_COLOR).find_or_create_by(title: label_name.strip)
|
||||
label = project.labels.create_with(color: Label::DEFAULT_COLOR).
|
||||
find_or_create_by(title: label_name.strip)
|
||||
self.labels << label
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def filter_superceded_votes(votes, notes)
|
||||
filteredvotes = [] + votes
|
||||
|
||||
votes.each do |vote|
|
||||
if vote.superceded?(notes)
|
||||
filteredvotes.delete(vote)
|
||||
end
|
||||
end
|
||||
|
||||
filteredvotes
|
||||
end
|
||||
end
|
||||
|
||||
@@ -50,7 +50,7 @@ module Mentionable
|
||||
matches.each do |match|
|
||||
identifier = match.delete "@"
|
||||
if identifier == "all"
|
||||
users += project.team.members.flatten
|
||||
users.push(*project.team.members.flatten)
|
||||
else
|
||||
id = User.find_by(username: identifier).try(:id)
|
||||
users << User.find(id) unless id.blank?
|
||||
|
||||
@@ -28,6 +28,9 @@ class Group < Namespace
|
||||
|
||||
mount_uploader :avatar, AttachmentUploader
|
||||
|
||||
after_create :post_create_hook
|
||||
after_destroy :post_destroy_hook
|
||||
|
||||
def human_name
|
||||
name
|
||||
end
|
||||
@@ -94,6 +97,18 @@ class Group < Namespace
|
||||
ldap_cn.present?
|
||||
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}%")
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
# == Schema Information
|
||||
#
|
||||
# Table name: identities
|
||||
#
|
||||
# id :integer not null, primary key
|
||||
# extern_uid :string(255)
|
||||
# provider :string(255)
|
||||
# user_id :integer
|
||||
#
|
||||
|
||||
class Identity < ActiveRecord::Base
|
||||
belongs_to :user
|
||||
|
||||
validates :extern_uid, allow_blank: true, uniqueness: {scope: :provider}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -19,7 +19,7 @@ class Key < ActiveRecord::Base
|
||||
|
||||
belongs_to :user
|
||||
|
||||
before_validation :strip_white_space, :generate_fingerpint
|
||||
before_validation :strip_white_space, :generate_fingerprint
|
||||
|
||||
validates :title, presence: true, length: { within: 0..255 }
|
||||
validates :key, presence: true, length: { within: 0..5000 }, format: { with: /\A(ssh|ecdsa)-.*\Z/ }, uniqueness: true
|
||||
@@ -78,7 +78,7 @@ class Key < ActiveRecord::Base
|
||||
|
||||
private
|
||||
|
||||
def generate_fingerpint
|
||||
def generate_fingerprint
|
||||
self.fingerprint = nil
|
||||
return unless key.present?
|
||||
|
||||
|
||||
@@ -28,8 +28,9 @@ class GroupMember < Member
|
||||
scope :with_user, ->(user) { where(user_id: user.id) }
|
||||
scope :with_ldap_dn, -> { joins(user: :identities).where("identities.provider LIKE ?", 'ldap%') }
|
||||
|
||||
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
|
||||
@@ -43,8 +44,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
|
||||
@@ -53,6 +55,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
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
# iid :integer
|
||||
# description :text
|
||||
# position :integer default(0)
|
||||
# locked_at :datetime
|
||||
#
|
||||
|
||||
require Rails.root.join("app/models/commit")
|
||||
@@ -253,7 +254,8 @@ class MergeRequest < ActiveRecord::Base
|
||||
def closes_issues
|
||||
if target_branch == project.default_branch
|
||||
issues = commits.flat_map { |c| c.closes_issues(project) }
|
||||
issues += Gitlab::ClosingIssueExtractor.closed_by_message_in_project(description, project)
|
||||
issues.push(*Gitlab::ClosingIssueExtractor.
|
||||
closed_by_message_in_project(description, project))
|
||||
issues.uniq.sort_by(&:id)
|
||||
else
|
||||
[]
|
||||
@@ -333,7 +335,7 @@ class MergeRequest < ActiveRecord::Base
|
||||
end
|
||||
|
||||
# Return array of possible target branches
|
||||
# dependes on target project of MR
|
||||
# depends on target project of MR
|
||||
def target_branches
|
||||
if target_project.nil?
|
||||
[]
|
||||
@@ -343,7 +345,7 @@ class MergeRequest < ActiveRecord::Base
|
||||
end
|
||||
|
||||
# Return array of possible source branches
|
||||
# dependes on source project of MR
|
||||
# depends on source project of MR
|
||||
def source_branches
|
||||
if source_project.nil?
|
||||
[]
|
||||
|
||||
@@ -84,7 +84,7 @@ module Network
|
||||
skip += self.class.max_count
|
||||
end
|
||||
else
|
||||
# Cant't find the target commit in the repo.
|
||||
# Can't find the target commit in the repo.
|
||||
offset = 0
|
||||
end
|
||||
end
|
||||
@@ -226,7 +226,7 @@ module Network
|
||||
|
||||
reserved = []
|
||||
for day in time_range
|
||||
reserved += @reserved[day]
|
||||
reserved.push(*@reserved[day])
|
||||
end
|
||||
reserved.uniq!
|
||||
|
||||
|
||||
@@ -459,6 +459,26 @@ class Note < ActiveRecord::Base
|
||||
)
|
||||
end
|
||||
|
||||
def superceded?(notes)
|
||||
return false unless vote?
|
||||
|
||||
notes.each do |note|
|
||||
next if note == self
|
||||
|
||||
if note.vote? &&
|
||||
self[:author_id] == note[:author_id] &&
|
||||
self[:created_at] <= note[:created_at]
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
def vote?
|
||||
upvote? || downvote?
|
||||
end
|
||||
|
||||
def votable?
|
||||
for_issue? || (for_merge_request? && !for_diff_line?)
|
||||
end
|
||||
@@ -480,7 +500,7 @@ class Note < ActiveRecord::Base
|
||||
end
|
||||
|
||||
# FIXME: Hack for polymorphic associations with STI
|
||||
# For more information wisit http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#label-Polymorphic+Associations
|
||||
# For more information visit http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#label-Polymorphic+Associations
|
||||
def noteable_type=(sType)
|
||||
super(sType.to_s.classify.constantize.base_class.to_s)
|
||||
end
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
# merge_requests_enabled :boolean default(TRUE), not null
|
||||
# wiki_enabled :boolean default(TRUE), not null
|
||||
# namespace_id :integer
|
||||
# issues_tracker :string(255) default("gitlab"), not null
|
||||
# issues_tracker :string(255) default('gitlab'), not null
|
||||
# issues_tracker_id :string(255)
|
||||
# snippets_enabled :boolean default(TRUE), not null
|
||||
# last_activity_at :datetime
|
||||
@@ -24,8 +24,14 @@
|
||||
# import_status :string(255)
|
||||
# repository_size :float default(0.0)
|
||||
# star_count :integer default(0), not null
|
||||
# import_type :string(255)
|
||||
# import_source :string(255)
|
||||
# avatar :string(255)
|
||||
#
|
||||
|
||||
require 'carrierwave/orm/activerecord'
|
||||
require 'file_size_validator'
|
||||
|
||||
class Project < ActiveRecord::Base
|
||||
include Gitlab::ShellAdapter
|
||||
include Gitlab::VisibilityLevel
|
||||
@@ -47,8 +53,8 @@ class Project < ActiveRecord::Base
|
||||
attr_accessor :new_default_branch
|
||||
|
||||
# Relations
|
||||
belongs_to :creator, foreign_key: "creator_id", class_name: "User"
|
||||
belongs_to :group, -> { where(type: Group) }, foreign_key: "namespace_id"
|
||||
belongs_to :creator, foreign_key: 'creator_id', class_name: 'User'
|
||||
belongs_to :group, -> { where(type: Group) }, foreign_key: 'namespace_id'
|
||||
belongs_to :namespace
|
||||
|
||||
has_one :git_hook, dependent: :destroy
|
||||
@@ -71,21 +77,27 @@ class Project < ActiveRecord::Base
|
||||
has_one :bamboo_service, dependent: :destroy
|
||||
has_one :teamcity_service, dependent: :destroy
|
||||
has_one :pushover_service, dependent: :destroy
|
||||
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"
|
||||
has_many :merge_requests, dependent: :destroy, foreign_key: 'target_project_id'
|
||||
# Merge requests from source project should be kept when source project was removed
|
||||
has_many :fork_merge_requests, foreign_key: "source_project_id", class_name: MergeRequest
|
||||
has_many :fork_merge_requests, foreign_key: 'source_project_id', class_name: MergeRequest
|
||||
has_many :issues, -> { order 'issues.state DESC, issues.created_at DESC' }, dependent: :destroy
|
||||
has_many :labels, dependent: :destroy
|
||||
has_many :services, dependent: :destroy
|
||||
has_many :events, dependent: :destroy
|
||||
has_many :milestones, dependent: :destroy
|
||||
has_many :notes, dependent: :destroy
|
||||
has_many :snippets, dependent: :destroy, class_name: "ProjectSnippet"
|
||||
has_many :hooks, dependent: :destroy, class_name: "ProjectHook"
|
||||
has_many :snippets, dependent: :destroy, class_name: 'ProjectSnippet'
|
||||
has_many :hooks, dependent: :destroy, class_name: 'ProjectHook'
|
||||
has_many :protected_branches, dependent: :destroy
|
||||
has_many :project_members, dependent: :destroy, as: :source, class_name: 'ProjectMember'
|
||||
has_many :users, through: :project_members
|
||||
@@ -120,28 +132,31 @@ class Project < ActiveRecord::Base
|
||||
validates_uniqueness_of :name, scope: :namespace_id
|
||||
validates_uniqueness_of :path, scope: :namespace_id
|
||||
validates :import_url,
|
||||
format: { with: URI::regexp(%w(git http https)), message: "should be a valid url" },
|
||||
format: { with: URI::regexp(%w(git http https)), message: 'should be a valid url' },
|
||||
if: :import?
|
||||
validates :star_count, numericality: { greater_than_or_equal_to: 0 }
|
||||
validate :check_limit, on: :create
|
||||
validate :avatar_type,
|
||||
if: ->(project) { project.avatar && project.avatar_changed? }
|
||||
validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
|
||||
|
||||
mount_uploader :avatar, AttachmentUploader
|
||||
|
||||
# Scopes
|
||||
scope :without_user, ->(user) { where("projects.id NOT IN (:ids)", ids: user.authorized_projects.map(&:id) ) }
|
||||
scope :without_team, ->(team) { team.projects.present? ? where("projects.id NOT IN (:ids)", ids: team.projects.map(&:id)) : scoped }
|
||||
scope :not_in_group, ->(group) { where("projects.id NOT IN (:ids)", ids: group.project_ids ) }
|
||||
scope :in_team, ->(team) { where("projects.id IN (:ids)", ids: team.projects.map(&:id)) }
|
||||
scope :without_user, ->(user) { where('projects.id NOT IN (:ids)', ids: user.authorized_projects.map(&:id) ) }
|
||||
scope :without_team, ->(team) { team.projects.present? ? where('projects.id NOT IN (:ids)', ids: team.projects.map(&:id)) : scoped }
|
||||
scope :not_in_group, ->(group) { where('projects.id NOT IN (:ids)', ids: group.project_ids ) }
|
||||
scope :in_team, ->(team) { where('projects.id IN (:ids)', ids: team.projects.map(&:id)) }
|
||||
scope :in_namespace, ->(namespace) { where(namespace_id: namespace.id) }
|
||||
scope :in_group_namespace, -> { joins(:group) }
|
||||
scope :sorted_by_activity, -> { reorder("projects.last_activity_at DESC") }
|
||||
scope :sorted_by_stars, -> { reorder("projects.star_count DESC") }
|
||||
scope :sorted_by_activity, -> { reorder('projects.last_activity_at DESC') }
|
||||
scope :sorted_by_stars, -> { reorder('projects.star_count DESC') }
|
||||
scope :personal, ->(user) { where(namespace_id: user.namespace_id) }
|
||||
scope :joined, ->(user) { where("namespace_id != ?", user.namespace_id) }
|
||||
scope :joined, ->(user) { where('namespace_id != ?', user.namespace_id) }
|
||||
scope :public_only, -> { where(visibility_level: Project::PUBLIC) }
|
||||
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
|
||||
@@ -177,7 +192,7 @@ class Project < ActiveRecord::Base
|
||||
|
||||
def publicish(user)
|
||||
visibility_levels = [Project::PUBLIC]
|
||||
visibility_levels += [Project::INTERNAL] if user
|
||||
visibility_levels << Project::INTERNAL if user
|
||||
where(visibility_level: visibility_levels)
|
||||
end
|
||||
|
||||
@@ -186,26 +201,26 @@ class Project < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def active
|
||||
joins(:issues, :notes, :merge_requests).order("issues.created_at, notes.created_at, merge_requests.created_at DESC")
|
||||
joins(:issues, :notes, :merge_requests).order('issues.created_at, notes.created_at, merge_requests.created_at DESC')
|
||||
end
|
||||
|
||||
def search(query)
|
||||
joins(:namespace).where("projects.archived = ?", false).
|
||||
where("LOWER(projects.name) LIKE :query OR
|
||||
joins(:namespace).where('projects.archived = ?', false).
|
||||
where('LOWER(projects.name) LIKE :query OR
|
||||
LOWER(projects.path) LIKE :query OR
|
||||
LOWER(namespaces.name) LIKE :query OR
|
||||
LOWER(projects.description) LIKE :query",
|
||||
LOWER(projects.description) LIKE :query',
|
||||
query: "%#{query.try(:downcase)}%")
|
||||
end
|
||||
|
||||
def search_by_title(query)
|
||||
where("projects.archived = ?", false).where("LOWER(projects.name) LIKE :query", query: "%#{query.downcase}%")
|
||||
where('projects.archived = ?', false).where('LOWER(projects.name) LIKE :query', query: "%#{query.downcase}%")
|
||||
end
|
||||
|
||||
def find_with_namespace(id)
|
||||
return nil unless id.include?("/")
|
||||
return nil unless id.include?('/')
|
||||
|
||||
id = id.split("/")
|
||||
id = id.split('/')
|
||||
namespace = Namespace.find_by(path: id.first)
|
||||
return nil unless namespace
|
||||
|
||||
@@ -223,7 +238,7 @@ class Project < ActiveRecord::Base
|
||||
when 'recently_updated' then reorder('projects.updated_at DESC')
|
||||
when 'last_updated' then reorder('projects.updated_at ASC')
|
||||
when 'largest_repository' then reorder('projects.repository_size DESC')
|
||||
else reorder("namespaces.path, projects.name ASC")
|
||||
else reorder('namespaces.path, projects.name ASC')
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -273,19 +288,19 @@ class Project < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def to_param
|
||||
namespace.path + "/" + path
|
||||
namespace.path + '/' + path
|
||||
end
|
||||
|
||||
def web_url
|
||||
[gitlab_config.url, path_with_namespace].join("/")
|
||||
[gitlab_config.url, path_with_namespace].join('/')
|
||||
end
|
||||
|
||||
def web_url_without_protocol
|
||||
web_url.split("://")[1]
|
||||
web_url.split('://')[1]
|
||||
end
|
||||
|
||||
def build_commit_note(commit)
|
||||
notes.new(commit_id: commit.id, noteable_type: "Commit")
|
||||
notes.new(commit_id: commit.id, noteable_type: 'Commit')
|
||||
end
|
||||
|
||||
def last_activity
|
||||
@@ -301,19 +316,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
|
||||
@@ -328,7 +367,8 @@ 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 jira jenkins)
|
||||
emails_on_push gemnasium slack pushover buildbox bamboo teamcity jenkins jira redmine custom_issue_tracker
|
||||
)
|
||||
end
|
||||
|
||||
def gitlab_ci?
|
||||
@@ -343,12 +383,25 @@ class Project < ActiveRecord::Base
|
||||
@ci_service ||= ci_services.select(&:activated?).first
|
||||
end
|
||||
|
||||
def jira_tracker?
|
||||
self.issues_tracker == "jira"
|
||||
def jira_tracker?
|
||||
issues_tracker.to_param == 'jira'
|
||||
end
|
||||
|
||||
def redmine_tracker?
|
||||
self.issues_tracker == "redmine"
|
||||
issues_tracker.to_param == 'redmine'
|
||||
end
|
||||
|
||||
def avatar_type
|
||||
unless self.avatar.image?
|
||||
self.errors.add :avatar, 'only images allowed'
|
||||
end
|
||||
end
|
||||
|
||||
def avatar_in_git
|
||||
@avatar_file ||= 'logo.png' if repository.blob_at_branch('master', 'logo.png')
|
||||
@avatar_file ||= 'logo.jpg' if repository.blob_at_branch('master', 'logo.jpg')
|
||||
@avatar_file ||= 'logo.gif' if repository.blob_at_branch('master', 'logo.gif')
|
||||
@avatar_file
|
||||
end
|
||||
|
||||
# For compatibility with old code
|
||||
@@ -378,7 +431,7 @@ class Project < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def team_member_by_name_or_email(name = nil, email = nil)
|
||||
user = users.where("name like ? or email like ?", name, email).first
|
||||
user = users.where('name like ? or email like ?', name, email).first
|
||||
project_members.where(user: user) if user
|
||||
end
|
||||
|
||||
@@ -390,7 +443,7 @@ class Project < ActiveRecord::Base
|
||||
def name_with_namespace
|
||||
@name_with_namespace ||= begin
|
||||
if namespace
|
||||
namespace.human_name + " / " + name
|
||||
namespace.human_name + ' / ' + name
|
||||
else
|
||||
name
|
||||
end
|
||||
@@ -425,7 +478,7 @@ class Project < ActiveRecord::Base
|
||||
def valid_repo?
|
||||
repository.exists?
|
||||
rescue
|
||||
errors.add(:path, "Invalid repository path")
|
||||
errors.add(:path, 'Invalid repository path')
|
||||
false
|
||||
end
|
||||
|
||||
@@ -484,7 +537,7 @@ class Project < ActiveRecord::Base
|
||||
end
|
||||
|
||||
def http_url_to_repo
|
||||
[gitlab_config.url, "/", path_with_namespace, ".git"].join('')
|
||||
[gitlab_config.url, '/', path_with_namespace, '.git'].join('')
|
||||
end
|
||||
|
||||
# Check if current branch name is marked as protected in the system
|
||||
@@ -547,6 +600,7 @@ class Project < ActiveRecord::Base
|
||||
# Since we do cache @event we need to reset cache in special cases:
|
||||
# * when project was moved
|
||||
# * when project was renamed
|
||||
# * when the project avatar changes
|
||||
# Events cache stored like events/23-20130109142513.
|
||||
# The cache key includes updated_at timestamp.
|
||||
# Thus it will automatically generate a new fragment
|
||||
@@ -619,7 +673,7 @@ class Project < ActiveRecord::Base
|
||||
if gitlab_shell.add_repository(path_with_namespace)
|
||||
true
|
||||
else
|
||||
errors.add(:base, "Failed to create repository")
|
||||
errors.add(:base, 'Failed to create repository')
|
||||
false
|
||||
end
|
||||
end
|
||||
@@ -632,7 +686,7 @@ class Project < ActiveRecord::Base
|
||||
ProjectWiki.new(self, self.owner).wiki
|
||||
true
|
||||
rescue ProjectWiki::CouldNotCreateWikiError => ex
|
||||
errors.add(:base, "Failed create wiki")
|
||||
errors.add(:base, 'Failed create wiki')
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||