Commit 6373c84d authored by Jan-Willem van der Meer's avatar Jan-Willem van der Meer

Merge remote-tracking branch 'origin/master' into feature-multi-ldap-servers

Conflicts:
	app/models/user.rb
	config/gitlab.yml.example
	spec/lib/gitlab/ldap/access_spec.rb
parents d6cabd27 37169e54
v 7.4.0
- Refactored membership logic
- Improve error reporting on users API (Julien Bianchi)
- Refactor test coverage tools usage. Use SIMPLECOV=true to generate it locally
- Default branch is protected by default
- Increase unicorn timeout to 60 seconds
- Sort search autocomplete projects by stars count so most popular go first
- Add README to tab on project show page
- Do not delete tmp/repositories itself during clean-up, only its contents
- Support for backup uploads to remote storage
- Prevent notes polling when there are not notes
- API: Add support for forking a project via the API (Bernhard Kaindl)
- API: filter project issues by milestone (Julien Bianchi)
- Fail harder in the backup script
- Zen mode for wiki and milestones (Robert Schilling)
- Move Emoji parsing to html-pipeline-gitlab (Robert Schilling)
- Font Awesome 4.2 integration (Sullivan Senechal)
v 7.3.2
- Fix creating new file via web editor
- Use gitlab-shell v2.0.1
v 7.3.1
- Fix ref parsing in Gitlab::GitAccess
- Fix error 500 when viewing diff on a file with changed permissions
- Fix adding comments to MR when source branch is master
- Fix error 500 when searching description contains relative link
v 7.3.0 v 7.3.0
- Always set the 'origin' remote in satellite actions - Always set the 'origin' remote in satellite actions
- Write authorized_keys in tmp/ during tests - Write authorized_keys in tmp/ during tests
......
...@@ -61,7 +61,7 @@ If you can, please submit a merge request with the fix or improvements including ...@@ -61,7 +61,7 @@ If you can, please submit a merge request with the fix or improvements including
1. Fork the project on GitLab Cloud 1. Fork the project on GitLab Cloud
1. Create a feature branch 1. Create a feature branch
1. Write [tests](README.md#run-the-tests) and code 1. Write [tests](README.md#run-the-tests) and code
1. Add your changes to the [CHANGELOG](CHANGELOG) 1. Add your changes to the [CHANGELOG](CHANGELOG) insert your line at a [random point](doc/workflow/gitlab_flow.md#do-not-order-commits-with-rebase) in the current version
1. If you are changing the README, some documentation or other things which have no effect on the tests, add `[ci skip]` somewhere in the commit message 1. If you are changing the README, some documentation or other things which have no effect on the tests, add `[ci skip]` somewhere in the commit message
1. If you have multiple commits please combine them into one commit by [squashing them](http://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits) 1. If you have multiple commits please combine them into one commit by [squashing them](http://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)
1. Push the commit to your fork 1. Push the commit to your fork
......
...@@ -31,7 +31,7 @@ gem 'omniauth-shibboleth' ...@@ -31,7 +31,7 @@ gem 'omniauth-shibboleth'
# Extracting information from a git repository # Extracting information from a git repository
# Provide access to Gitlab::Git library # Provide access to Gitlab::Git library
gem "gitlab_git", '~> 6.0' gem "gitlab_git", '7.0.0.rc8'
# Ruby/Rack Git Smart-HTTP Server Handler # Ruby/Rack Git Smart-HTTP Server Handler
gem 'gitlab-grack', '~> 2.0.0.pre', require: 'grack' gem 'gitlab-grack', '~> 2.0.0.pre', require: 'grack'
...@@ -71,8 +71,8 @@ gem "carrierwave" ...@@ -71,8 +71,8 @@ gem "carrierwave"
gem 'dropzonejs-rails' gem 'dropzonejs-rails'
# for aws storage # for aws storage
gem "fog", "~> 1.14", group: :aws gem "fog", "~> 1.14"
gem "unf", group: :aws gem "unf"
# Authorization # Authorization
gem "six" gem "six"
...@@ -80,6 +80,9 @@ gem "six" ...@@ -80,6 +80,9 @@ gem "six"
# Seed data # Seed data
gem "seed-fu" gem "seed-fu"
# Markup pipeline for GitLab
gem 'html-pipeline-gitlab', '~> 0.1.0'
# Markdown to HTML # Markdown to HTML
gem "github-markup" gem "github-markup"
...@@ -87,7 +90,7 @@ gem "github-markup" ...@@ -87,7 +90,7 @@ gem "github-markup"
gem 'redcarpet', '~> 3.1.2' gem 'redcarpet', '~> 3.1.2'
gem 'RedCloth' gem 'RedCloth'
gem 'rdoc', '~>3.6' gem 'rdoc', '~>3.6'
gem 'org-ruby' gem 'org-ruby', '= 0.9.9'
gem 'creole', '~>0.3.6' gem 'creole', '~>0.3.6'
gem 'wikicloth', '=0.8.1' gem 'wikicloth', '=0.8.1'
gem 'asciidoctor', '= 0.1.4' gem 'asciidoctor', '= 0.1.4'
...@@ -158,7 +161,7 @@ gem "rack-attack" ...@@ -158,7 +161,7 @@ gem "rack-attack"
# Ace editor # Ace editor
gem 'ace-rails-ap' gem 'ace-rails-ap'
# Keyboard shortcuts # Keyboard shortcuts
gem 'mousetrap-rails' gem 'mousetrap-rails'
# Semantic UI Sass for Sidebar # Semantic UI Sass for Sidebar
...@@ -178,7 +181,7 @@ gem "jquery-ui-rails" ...@@ -178,7 +181,7 @@ gem "jquery-ui-rails"
gem "jquery-scrollto-rails" gem "jquery-scrollto-rails"
gem "raphael-rails", "~> 2.1.2" gem "raphael-rails", "~> 2.1.2"
gem 'bootstrap-sass', '~> 3.0' gem 'bootstrap-sass', '~> 3.0'
gem "font-awesome-rails", '~> 3.2' gem "font-awesome-rails", '~> 4.2'
gem "gitlab_emoji", "~> 0.0.1.1" gem "gitlab_emoji", "~> 0.0.1.1"
gem "gon", '~> 5.0.0' gem "gon", '~> 5.0.0'
gem 'nprogress-rails' gem 'nprogress-rails'
......
...@@ -106,7 +106,7 @@ GEM ...@@ -106,7 +106,7 @@ GEM
devise (~> 3.2) devise (~> 3.2)
diff-lcs (1.2.5) diff-lcs (1.2.5)
diffy (3.0.3) diffy (3.0.3)
docile (1.1.1) docile (1.1.5)
dotenv (0.9.0) dotenv (0.9.0)
dropzonejs-rails (0.4.14) dropzonejs-rails (0.4.14)
rails (> 3.1) rails (> 3.1)
...@@ -152,7 +152,7 @@ GEM ...@@ -152,7 +152,7 @@ GEM
net-ssh (>= 2.1.3) net-ssh (>= 2.1.3)
fog-json (1.0.0) fog-json (1.0.0)
multi_json (~> 1.0) multi_json (~> 1.0)
font-awesome-rails (3.2.1.3) font-awesome-rails (4.2.0.0)
railties (>= 3.2, < 5.0) railties (>= 3.2, < 5.0)
foreman (0.63.0) foreman (0.63.0)
dotenv (>= 0.7) dotenv (>= 0.7)
...@@ -179,10 +179,9 @@ GEM ...@@ -179,10 +179,9 @@ GEM
mime-types (~> 1.19) mime-types (~> 1.19)
gitlab_emoji (0.0.1.1) gitlab_emoji (0.0.1.1)
emoji (~> 1.0.1) emoji (~> 1.0.1)
gitlab_git (6.2.1) gitlab_git (7.0.0.rc8)
activesupport (~> 4.0) activesupport (~> 4.0)
charlock_holmes (~> 0.6) charlock_holmes (~> 0.6)
gitlab-grit (~> 2.6)
gitlab-linguist (~> 3.0) gitlab-linguist (~> 3.0)
rugged (~> 0.21.0) rugged (~> 0.21.0)
gitlab_meta (7.0) gitlab_meta (7.0)
...@@ -239,6 +238,12 @@ GEM ...@@ -239,6 +238,12 @@ GEM
hipchat (0.14.0) hipchat (0.14.0)
httparty httparty
httparty httparty
html-pipeline (1.11.0)
activesupport (>= 2)
nokogiri (~> 1.4)
html-pipeline-gitlab (0.1.0)
gitlab_emoji (~> 0.0.1.1)
html-pipeline (~> 1.11.0)
http_parser.rb (0.5.3) http_parser.rb (0.5.3)
httparty (0.13.0) httparty (0.13.0)
json (~> 1.8) json (~> 1.8)
...@@ -327,7 +332,7 @@ GEM ...@@ -327,7 +332,7 @@ GEM
omniauth-twitter (1.0.1) omniauth-twitter (1.0.1)
multi_json (~> 1.3) multi_json (~> 1.3)
omniauth-oauth (~> 1.0) omniauth-oauth (~> 1.0)
org-ruby (0.9.8) org-ruby (0.9.9)
rubypants (~> 0.2) rubypants (~> 0.2)
orm_adapter (0.5.0) orm_adapter (0.5.0)
pg (0.15.1) pg (0.15.1)
...@@ -471,7 +476,7 @@ GEM ...@@ -471,7 +476,7 @@ GEM
redis (>= 3.0.4) redis (>= 3.0.4)
redis-namespace (>= 1.3.1) redis-namespace (>= 1.3.1)
simple_oauth (0.1.9) simple_oauth (0.1.9)
simplecov (0.8.2) simplecov (0.9.0)
docile (~> 1.1.0) docile (~> 1.1.0)
multi_json multi_json
simplecov-html (~> 0.8.0) simplecov-html (~> 0.8.0)
...@@ -609,7 +614,7 @@ DEPENDENCIES ...@@ -609,7 +614,7 @@ DEPENDENCIES
factory_girl_rails factory_girl_rails
ffaker ffaker
fog (~> 1.14) fog (~> 1.14)
font-awesome-rails (~> 3.2) font-awesome-rails (~> 4.2)
foreman foreman
gemnasium-gitlab-service (~> 0.2) gemnasium-gitlab-service (~> 0.2)
github-markup github-markup
...@@ -617,7 +622,7 @@ DEPENDENCIES ...@@ -617,7 +622,7 @@ DEPENDENCIES
gitlab-grack (~> 2.0.0.pre) gitlab-grack (~> 2.0.0.pre)
gitlab-linguist (~> 3.0.0) gitlab-linguist (~> 3.0.0)
gitlab_emoji (~> 0.0.1.1) gitlab_emoji (~> 0.0.1.1)
gitlab_git (~> 6.0) gitlab_git (= 7.0.0.rc8)
gitlab_meta (= 7.0) gitlab_meta (= 7.0)
gitlab_omniauth-ldap (= 1.1.0) gitlab_omniauth-ldap (= 1.1.0)
gollum-lib (~> 3.0.0) gollum-lib (~> 3.0.0)
...@@ -629,6 +634,7 @@ DEPENDENCIES ...@@ -629,6 +634,7 @@ DEPENDENCIES
guard-spinach guard-spinach
haml-rails haml-rails
hipchat (~> 0.14.0) hipchat (~> 0.14.0)
html-pipeline-gitlab (~> 0.1.0)
httparty httparty
jasmine (= 2.0.2) jasmine (= 2.0.2)
jquery-atwho-rails (~> 0.3.3) jquery-atwho-rails (~> 0.3.3)
...@@ -650,7 +656,7 @@ DEPENDENCIES ...@@ -650,7 +656,7 @@ DEPENDENCIES
omniauth-google-oauth2 omniauth-google-oauth2
omniauth-shibboleth omniauth-shibboleth
omniauth-twitter omniauth-twitter
org-ruby org-ruby (= 0.9.9)
pg pg
poltergeist (~> 1.5.1) poltergeist (~> 1.5.1)
pry pry
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
- [![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq) - [![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq)
- [![Coverage Status](https://coveralls.io/repos/gitlabhq/gitlabhq/badge.png?branch=master)](https://coveralls.io/r/gitlabhq/gitlabhq) - [![Coverage Status](https://coveralls.io/repos/gitlabhq/gitlabhq/badge.png?branch=master)](https://coveralls.io/r/gitlabhq/gitlabhq?branch=master)
- [![PullReview stats](https://www.pullreview.com/gitlab/gitlab-org/gitlab-ce/badges/master.svg?)](https://www.pullreview.com/gitlab.gitlab.com/gitlab-org/gitlab-ce/reviews/master) - [![PullReview stats](https://www.pullreview.com/gitlab/gitlab-org/gitlab-ce/badges/master.svg?)](https://www.pullreview.com/gitlab.gitlab.com/gitlab-org/gitlab-ce/reviews/master)
...@@ -38,17 +38,6 @@ On [about.gitlab.com](https://about.gitlab.com/) you can find more information a ...@@ -38,17 +38,6 @@ On [about.gitlab.com](https://about.gitlab.com/) you can find more information a
- [GitLab Enterprise Edition](https://about.gitlab.com/gitlab-ee/) with additional features aimed at larger organizations. - [GitLab Enterprise Edition](https://about.gitlab.com/gitlab-ee/) with additional features aimed at larger organizations.
- [GitLab CI](https://about.gitlab.com/gitlab-ci/) a continuous integration (CI) server that is easy to integrate with GitLab. - [GitLab CI](https://about.gitlab.com/gitlab-ci/) a continuous integration (CI) server that is easy to integrate with GitLab.
## Third-party applications
Access GitLab from multiple platforms with applications below.
These applications are maintained by contributors, GitLab B.V. does not offer support for them.
- [iPhone app](http://gitlabcontrol.com/)
- [Android app](https://play.google.com/store/apps/details?id=com.bd.gitlab&hl=en)
- [Chrome app](https://chrome.google.com/webstore/detail/chrome-gitlab-notifier/eageapgbnjicdjjihgclpclilenjbobi)
- [Command line client](https://github.com/drewblessing/gitlab-cli)
- [Ruby API wrapper](https://github.com/NARKOZ/gitlab)
## Requirements ## Requirements
- Ubuntu/Debian/CentOS/RHEL** - Ubuntu/Debian/CentOS/RHEL**
...@@ -61,7 +50,19 @@ These applications are maintained by contributors, GitLab B.V. does not offer su ...@@ -61,7 +50,19 @@ These applications are maintained by contributors, GitLab B.V. does not offer su
## Installation ## Installation
Please see [the installation page on the GitLab website](https://about.gitlab.com/installation/). Please see [the installation page on the GitLab website](https://about.gitlab.com/installation/) for the various options.
Since a manual installation is a lot of work and error prone we strongly recommend fast and reliable Omnibus package installation (deb/rpm) on that page.
## Third-party applications
Access GitLab from multiple platforms with applications below.
These applications are maintained by contributors, GitLab B.V. does not offer support for them.
- [iPhone app](http://gitlabcontrol.com/)
- [Android app](https://play.google.com/store/apps/details?id=com.bd.gitlab&hl=en)
- [Chrome app](https://chrome.google.com/webstore/detail/chrome-gitlab-notifier/eageapgbnjicdjjihgclpclilenjbobi)
- [Command line client](https://github.com/drewblessing/gitlab-cli)
- [Ruby API wrapper](https://github.com/NARKOZ/gitlab)
### New versions ### New versions
......
...@@ -46,10 +46,10 @@ class Admin ...@@ -46,10 +46,10 @@ class Admin
modal.hide() modal.hide()
$('.change-owner-link').show() $('.change-owner-link').show()
$('li.users_project').bind 'ajax:success', -> $('li.project_member').bind 'ajax:success', ->
Turbolinks.visit(location.href) Turbolinks.visit(location.href)
$('li.users_group').bind 'ajax:success', -> $('li.group_member').bind 'ajax:success', ->
Turbolinks.visit(location.href) Turbolinks.visit(location.href)
@Admin = Admin @Admin = Admin
...@@ -38,7 +38,7 @@ ...@@ -38,7 +38,7 @@
# Return users list. Filtered by query # Return users list. Filtered by query
# Only active users retrieved # Only active users retrieved
users: (query, callback) -> users: (query, skip_ldap, callback) ->
url = Api.buildUrl(Api.users_path) url = Api.buildUrl(Api.users_path)
$.ajax( $.ajax(
...@@ -48,6 +48,7 @@ ...@@ -48,6 +48,7 @@
search: query search: query
per_page: 20 per_page: 20
active: true active: true
skip_ldap: skip_ldap
dataType: "json" dataType: "json"
).done (users) -> ).done (users) ->
callback(users) callback(users)
......
...@@ -15,13 +15,14 @@ ...@@ -15,13 +15,14 @@
#= require jquery.atwho #= require jquery.atwho
#= require jquery.scrollTo #= require jquery.scrollTo
#= require jquery.blockUI #= require jquery.blockUI
#= require turbolinks
#= require jquery.turbolinks #= require jquery.turbolinks
#= require turbolinks
#= require bootstrap #= require bootstrap
#= require select2 #= require select2
#= require raphael #= require raphael
#= require g.raphael-min #= require g.raphael-min
#= require g.bar-min #= require g.bar-min
#= require chart-lib.min
#= require branch-graph #= require branch-graph
#= require highlight.pack #= require highlight.pack
#= require ace/ace #= require ace/ace
...@@ -149,7 +150,6 @@ $ -> ...@@ -149,7 +150,6 @@ $ ->
if (flash = $(".flash-container")).length > 0 if (flash = $(".flash-container")).length > 0
flash.click -> $(@).fadeOut() flash.click -> $(@).fadeOut()
flash.show() flash.show()
setTimeout (-> flash.fadeOut()), 5000
# Disable form buttons while a form is submitting # Disable form buttons while a form is submitting
$('body').on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) -> $('body').on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) ->
...@@ -172,8 +172,8 @@ $ -> ...@@ -172,8 +172,8 @@ $ ->
# Show/hide comments on diff # Show/hide comments on diff
$("body").on "click", ".js-toggle-diff-comments", (e) -> $("body").on "click", ".js-toggle-diff-comments", (e) ->
$(@).find('i'). $(@).find('i').
toggleClass('icon-chevron-down'). toggleClass('fa fa-chevron-down').
toggleClass('icon-chevron-up') toggleClass('fa fa-chevron-up')
$(@).closest(".diff-file").find(".notes_holder").toggle() $(@).closest(".diff-file").find(".notes_holder").toggle()
e.preventDefault() e.preventDefault()
......
...@@ -8,7 +8,7 @@ $ -> ...@@ -8,7 +8,7 @@ $ ->
# #
$("body").on "click", ".js-toggle-button", (e) -> $("body").on "click", ".js-toggle-button", (e) ->
$(@).find('i'). $(@).find('i').
toggleClass('icon-chevron-down'). toggleClass('fa fa-chevron-down').
toggleClass('icon-chevron-up') toggleClass('fa fa-chevron-up')
$(@).closest(".js-toggle-container").find(".js-toggle-content").toggle() $(@).closest(".js-toggle-container").find(".js-toggle-content").toggle()
e.preventDefault() e.preventDefault()
...@@ -90,11 +90,15 @@ class @BranchGraph ...@@ -90,11 +90,15 @@ class @BranchGraph
renderPartialGraph: -> renderPartialGraph: ->
start = Math.floor((@element.scrollTop() - @offsetY) / @unitTime) - 10 start = Math.floor((@element.scrollTop() - @offsetY) / @unitTime) - 10
start = 0 if start < 0 if start < 0
isGraphEdge = true
start = 0
end = start + 40 end = start + 40
end = @commits.length if @commits.length < end if @commits.length < end
isGraphEdge = true
end = @commits.length
if @prev_start == -1 or Math.abs(@prev_start - start) > 10 if @prev_start == -1 or Math.abs(@prev_start - start) > 10 or isGraphEdge
i = start i = start
@prev_start = start @prev_start = start
......
@Chart =
labels: []
values: []
init: (labels, values, title) ->
r = Raphael('activity-chart')
fin = ->
@flag = r.popup(@bar.x, @bar.y, @bar.value or "0").insertBefore(this)
fout = ->
@flag.animate
opacity: 0, 300, -> @remove()
r.text(160, 10, title).attr font: "13px sans-serif"
r.barchart(
10, 20, 560, 200,
[values],
{colors:["#456"]}
).label(labels, true)
.hover(fin, fout)
...@@ -27,6 +27,8 @@ class Dispatcher ...@@ -27,6 +27,8 @@ class Dispatcher
new ZenMode() new ZenMode()
when 'projects:milestones:show' when 'projects:milestones:show'
new Milestone() new Milestone()
when 'projects:milestones:new'
new ZenMode()
when 'projects:issues:new','projects:issues:edit' when 'projects:issues:new','projects:issues:edit'
GitLab.GfmAutoComplete.setup() GitLab.GfmAutoComplete.setup()
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
...@@ -87,6 +89,7 @@ class Dispatcher ...@@ -87,6 +89,7 @@ class Dispatcher
when 'wikis' when 'wikis'
new Wikis() new Wikis()
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
new ZenMode()
when 'snippets', 'labels', 'graphs' when 'snippets', 'labels', 'graphs'
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
when 'team_members', 'deploy_keys', 'hooks', 'services', 'protected_branches' when 'team_members', 'deploy_keys', 'hooks', 'services', 'protected_branches'
......
...@@ -10,6 +10,5 @@ class Flash ...@@ -10,6 +10,5 @@ class Flash
flash.click -> $(@).fadeOut() flash.click -> $(@).fadeOut()
flash.show() flash.show()
setTimeout (-> flash.fadeOut()), 5000
@Flash = Flash @Flash = Flash
class GroupMembers class GroupMembers
constructor: -> constructor: ->
$('li.users_group').bind 'ajax:success', -> $('li.group_member').bind 'ajax:success', ->
$(this).fadeOut() $(this).fadeOut()
@GroupMembers = GroupMembers @GroupMembers = GroupMembers
......
...@@ -7,18 +7,18 @@ $(document).ready -> ...@@ -7,18 +7,18 @@ $(document).ready ->
divHover = "<div class=\"div-dropzone-hover\"></div>" divHover = "<div class=\"div-dropzone-hover\"></div>"
divSpinner = "<div class=\"div-dropzone-spinner\"></div>" divSpinner = "<div class=\"div-dropzone-spinner\"></div>"
divAlert = "<div class=\"" + alertClass + "\"></div>" divAlert = "<div class=\"" + alertClass + "\"></div>"
iconPicture = "<i class=\"icon-picture div-dropzone-icon\"></i>" iconPicture = "<i class=\"fa fa-picture-o div-dropzone-icon\"></i>"
iconSpinner = "<i class=\"icon-spinner icon-spin div-dropzone-icon\"></i>" iconSpinner = "<i class=\"fa fa-spinner fa-spin div-dropzone-icon\"></i>"
btnAlert = "<button type=\"button\"" + alertAttr + ">&times;</button>" btnAlert = "<button type=\"button\"" + alertAttr + ">&times;</button>"
project_image_path_upload = window.project_image_path_upload or null project_image_path_upload = window.project_image_path_upload or null
$("textarea.markdown-area").wrap "<div class=\"div-dropzone\"></div>" $("textarea.markdown-area").wrap "<div class=\"div-dropzone\"></div>"
$(".div-dropzone").parent().addClass "div-dropzone-wrapper" $(".div-dropzone").parent().addClass "div-dropzone-wrapper"
$(".div-dropzone").append divHover $(".div-dropzone").append divHover
$(".div-dropzone-hover").append iconPicture $(".div-dropzone-hover").append iconPicture
$(".div-dropzone").append divSpinner $(".div-dropzone").append divSpinner
$(".div-dropzone-spinner").append iconSpinner $(".div-dropzone-spinner").append iconSpinner
$(".div-dropzone-spinner").css $(".div-dropzone-spinner").css
"opacity": 0 "opacity": 0
...@@ -27,12 +27,12 @@ $(document).ready -> ...@@ -27,12 +27,12 @@ $(document).ready ->
dropzone = $(".div-dropzone").dropzone( dropzone = $(".div-dropzone").dropzone(
url: project_image_path_upload url: project_image_path_upload
dictDefaultMessage: "" dictDefaultMessage: ""
clickable: false clickable: true
paramName: "markdown_img" paramName: "markdown_img"
maxFilesize: 10 maxFilesize: 10
uploadMultiple: false uploadMultiple: false
acceptedFiles: "image/jpg,image/jpeg,image/gif,image/png" acceptedFiles: "image/jpg,image/jpeg,image/gif,image/png"
headers: headers:
"X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content") "X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content")
previewContainer: false previewContainer: false
...@@ -91,7 +91,7 @@ $(document).ready -> ...@@ -91,7 +91,7 @@ $(document).ready ->
handlePaste = (e) -> handlePaste = (e) ->
e.preventDefault() e.preventDefault()
my_event = e.originalEvent my_event = e.originalEvent
if my_event.clipboardData and my_event.clipboardData.items if my_event.clipboardData and my_event.clipboardData.items
processItem(my_event) processItem(my_event)
...@@ -115,7 +115,7 @@ $(document).ready -> ...@@ -115,7 +115,7 @@ $(document).ready ->
return item return item
i++ i++
return false return false
pasteText = (text) -> pasteText = (text) ->
caretStart = $(child)[0].selectionStart caretStart = $(child)[0].selectionStart
caretEnd = $(child)[0].selectionEnd caretEnd = $(child)[0].selectionEnd
...@@ -126,12 +126,12 @@ $(document).ready -> ...@@ -126,12 +126,12 @@ $(document).ready ->
$(child).val beforeSelection + text + afterSelection $(child).val beforeSelection + text + afterSelection
$(".markdown-area").trigger "input" $(".markdown-area").trigger "input"
getFilename = (e) -> getFilename = (e) ->
if window.clipboardData and window.clipboardData.getData if window.clipboardData and window.clipboardData.getData
value = window.clipboardData.getData("Text") value = window.clipboardData.getData("Text")
else if e.clipboardData and e.clipboardData.getData else if e.clipboardData and e.clipboardData.getData
value = e.clipboardData.getData("text/plain") value = e.clipboardData.getData("text/plain")
value = value.split("\r") value = value.split("\r")
value.first() value.first()
...@@ -154,7 +154,7 @@ $(document).ready -> ...@@ -154,7 +154,7 @@ $(document).ready ->
success: (e, textStatus, response) -> success: (e, textStatus, response) ->
insertToTextArea(filename, formatLink(response.responseJSON.link)) insertToTextArea(filename, formatLink(response.responseJSON.link))
error: (response) -> error: (response) ->
showError(response.responseJSON.message) showError(response.responseJSON.message)
...@@ -190,7 +190,7 @@ $(document).ready -> ...@@ -190,7 +190,7 @@ $(document).ready ->
$(".markdown-selector").click (e) -> $(".markdown-selector").click (e) ->
e.preventDefault() e.preventDefault()
$(@).closest(".div-dropzone-wrapper").find(".div-dropzone").click() $(@).closest('.gfm-form').find('.div-dropzone').click()
return return
return return
...@@ -15,7 +15,7 @@ class MergeRequest ...@@ -15,7 +15,7 @@ class MergeRequest
modal = $('#modal_merge_info').modal(show: false) modal = $('#modal_merge_info').modal(show: false)
disableButtonIfEmptyField '#merge_commit_message', '.accept_merge_request' disableButtonIfEmptyField '#commit_message', '.accept_merge_request'
# Local jQuery finder # Local jQuery finder
...@@ -40,6 +40,8 @@ class MergeRequest ...@@ -40,6 +40,8 @@ class MergeRequest
if @opts.ci_enable if @opts.ci_enable
$.get @opts.url_to_ci_check, (data) => $.get @opts.url_to_ci_check, (data) =>
this.showCiState data.status this.showCiState data.status
if data.coverage
this.showCiCoverage data.coverage
, 'json' , 'json'
bindEvents: -> bindEvents: ->
...@@ -102,7 +104,11 @@ class MergeRequest ...@@ -102,7 +104,11 @@ class MergeRequest
when "running", "pending" when "running", "pending"
$('.mr-state-widget').addClass("panel-warning") $('.mr-state-widget').addClass("panel-warning")
showCiCoverage: (coverage) ->
cov_html = $('<span>')
cov_html.addClass('ci-coverage')
cov_html.text('Coverage ' + coverage + '%')
$('.ci_widget:visible').append(cov_html)
loadDiff: (event) -> loadDiff: (event) ->
$.ajax $.ajax
......
...@@ -6,6 +6,7 @@ class Notes ...@@ -6,6 +6,7 @@ class Notes
@notes_url = gon.relative_url_root + @notes_url if gon.relative_url_root? @notes_url = gon.relative_url_root + @notes_url if gon.relative_url_root?
@note_ids = note_ids @note_ids = note_ids
@last_fetched_at = last_fetched_at @last_fetched_at = last_fetched_at
@noteable_url = document.URL
@initRefresh() @initRefresh()
@setupMainTargetNoteForm() @setupMainTargetNoteForm()
@cleanBinding() @cleanBinding()
...@@ -95,7 +96,8 @@ class Notes ...@@ -95,7 +96,8 @@ class Notes
, 15000 , 15000
refresh: -> refresh: ->
@getContent() unless document.hidden unless document.hidden or (@noteable_url != document.URL)
@getContent()
getContent: -> getContent: ->
$.ajax $.ajax
......
...@@ -59,3 +59,12 @@ $ -> ...@@ -59,3 +59,12 @@ $ ->
$(@).toggleClass('on').find('.count').html(data.star_count) $(@).toggleClass('on').find('.count').html(data.star_count)
.on 'ajax:error', (e, xhr, status, error) -> .on 'ajax:error', (e, xhr, status, error) ->
new Flash('Star toggle failed. Try again later.', 'alert') 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")
defaultView = $.cookie("default_view")
if defaultView
$("a[href=" + defaultView + "]").tab "show"
else
$("a[data-toggle='tab']:first").tab "show"
...@@ -24,22 +24,7 @@ class window.ContributorsStatGraph ...@@ -24,22 +24,7 @@ class window.ContributorsStatGraph
class: 'graph-author-commits-count' class: 'graph-author-commits-count'
}) })
commits.text(author.commits + " commits") commits.text(author.commits + " commits")
additions = $('<span/>', {
class: 'graph-additions'
})
additions.text(author.additions + " ++")
deletions = $('<span/>', {
class: 'graph-deletions'
})
deletions.text(author.deletions + " --")
$('<span/>').append(commits) $('<span/>').append(commits)
.append(" / ")
.append(additions)
.append(" / ")
.append(deletions)
create_author_header: (author) -> create_author_header: (author) ->
list_item = $('<li/>', { list_item = $('<li/>', {
......
...@@ -90,4 +90,4 @@ window.ContributorsStatGraphUtil = ...@@ -90,4 +90,4 @@ window.ContributorsStatGraphUtil =
true true
else else
false false
...@@ -15,12 +15,14 @@ $ -> ...@@ -15,12 +15,14 @@ $ ->
user.name user.name
$('.ajax-users-select').each (i, select) -> $('.ajax-users-select').each (i, select) ->
skip_ldap = $(select).hasClass('skip_ldap')
$(select).select2 $(select).select2
placeholder: "Search for a user" placeholder: "Search for a user"
multiple: $(select).hasClass('multiselect') multiple: $(select).hasClass('multiselect')
minimumInputLength: 0 minimumInputLength: 0
query: (query) -> query: (query) ->
Api.users query.term, (users) -> Api.users query.term, skip_ldap, (users) ->
data = { results: users } data = { results: users }
query.callback(data) query.callback(data)
......
...@@ -32,6 +32,8 @@ class @ZenMode ...@@ -32,6 +32,8 @@ class @ZenMode
@active_zen_area = @active_checkbox.parent().find('textarea') @active_zen_area = @active_checkbox.parent().find('textarea')
@active_zen_area.focus() @active_zen_area.focus()
window.location.hash = ZenMode.fullscreen_prefix + @active_checkbox.prop('id') window.location.hash = ZenMode.fullscreen_prefix + @active_checkbox.prop('id')
# Disable dropzone in ZEN mode
Dropzone.forElement('.div-dropzone').disable()
exitZenMode: => exitZenMode: =>
if @active_zen_area isnt null if @active_zen_area isnt null
...@@ -41,6 +43,8 @@ class @ZenMode ...@@ -41,6 +43,8 @@ class @ZenMode
@active_checkbox = null @active_checkbox = null
window.location.hash = '' window.location.hash = ''
window.scrollTo(window.pageXOffset, @scroll_position) window.scrollTo(window.pageXOffset, @scroll_position)
# Enable dropzone when leaving ZEN mode
Dropzone.forElement('.div-dropzone').enable()
checkboxFromLocationHash: (e) -> checkboxFromLocationHash: (e) ->
id = $.trim(window.location.hash.replace('#' + ZenMode.fullscreen_prefix, '')) id = $.trim(window.location.hash.replace('#' + ZenMode.fullscreen_prefix, ''))
......
...@@ -60,7 +60,6 @@ ...@@ -60,7 +60,6 @@
.highlight { .highlight {
margin-bottom: 9px; margin-bottom: 9px;
@include border-radius(4px);
> pre { > pre {
margin: 0; margin: 0;
......
.flash-container { .flash-container {
display: none;
cursor: pointer; cursor: pointer;
margin: 0; margin: 0;
text-align: center;
color: #fff;
font-size: 14px; font-size: 14px;
position: fixed;
bottom: 0;
width: 100%; width: 100%;
opacity: 0.8;
z-index: 100; z-index: 100;
.flash-notice { .flash-notice {
background: #49C; @extend .alert;
padding: 10px; @extend .alert-info;
text-shadow: 0 1px 1px #178;
} }
.flash-alert { .flash-alert {
background: #C67; @extend .alert;
text-shadow: 0 1px 1px #945; @extend .alert-danger;
padding: 10px;
} }
} }
textarea {
resize: vertical;
}
input[type='search'].search-text-input { input[type='search'].search-text-input {
background-image: image-url("icon-search.png"); background-image: image-url("icon-search.png");
background-repeat: no-repeat; background-repeat: no-repeat;
......
/**
* Styles that apply to all GFM related forms.
*/
.issue-form, .merge-request-form, .wiki-form {
.description {
height: 20em;
}
}
.wiki-form {
.description {
height: 26em;
}
}
.milestone-form {
.description {
height: 14em;
}
}
\ No newline at end of file
/**
* Styles that apply to both issues and merge requests.
*/
.issue-form, .merge-request-form {
.description {
height: 20em;
}
}
...@@ -8,43 +8,51 @@ ...@@ -8,43 +8,51 @@
*/ */
.issue-box { .issue-box {
color: #666; color: #555;
margin:20px 0; margin:20px 0;
background: #FFF; background: #f9f9f9;
border: 1px solid #EEE; border-top-left-radius: 5px;
@include box-shadow(0 1px 1px rgba(0, 0, 0, 0.05)); @include box-shadow(0 1px 1px rgba(0, 0, 0, 0.09));
&.issue-box-closed { &.issue-box-closed {
border-color: $border_danger;
.state { .state {
background-color: #F3CECE;
border-color: $border_danger;
}
.state-label {
background-color: $bg_danger; background-color: $bg_danger;
color: #FFF; color: #FFF;
border-color: $border_danger;
} }
} }
&.issue-box-merged { &.issue-box-merged {
border-color: $border_primary;
.state { .state {
background-color: #B7CEE7;
border-color: $border_primary;
}
.state-label {
background-color: $bg_primary; background-color: $bg_primary;
color: #FFF; color: #FFF;
border-color: $border_primary;
} }
} }
&.issue-box-open { &.issue-box-open {
border-color: $border_success;
.state { .state {
border-color: $border_success; background-color: #D6F1D7;
border-color: $bg_success;
}
.state-label {
background-color: $bg_success; background-color: $bg_success;
color: #FFF; color: #FFF;
} }
} }
&.issue-box-expired { &.issue-box-expired {
border-color: #cea61b;
.state { .state {
background-color: #EEE9B3;
border-color: #faebcc; border-color: #faebcc;
}
.state-label {
background: #cea61b; background: #cea61b;
color: #FFF; color: #FFF;
} }
...@@ -55,8 +63,7 @@ ...@@ -55,8 +63,7 @@
} }
.state { .state {
border-bottom: 1px solid #DDD; background-color: #f9f9f9;
padding: 10px 15px;
} }
.title { .title {
...@@ -104,12 +111,14 @@ ...@@ -104,12 +111,14 @@
font-size: 14px; font-size: 14px;
float: left; float: left;
font-weight: bold; font-weight: bold;
padding: 10px 15px;
border-top-left-radius: 5px;
} }
.creator { .creator {
float: right; float: right;
padding: 10px 15px;
a { a {
color: #FFF;
text-decoration: underline; text-decoration: underline;
} }
} }
......
...@@ -124,7 +124,7 @@ $list-group-active-bg: $bg_primary; ...@@ -124,7 +124,7 @@ $list-group-active-bg: $bg_primary;
color: #888; color: #888;
text-shadow: 0 1px 1px #fff; text-shadow: 0 1px 1px #fff;
} }
i[class^="icon-"] { i[class~="fa"] {
line-height: 14px; line-height: 14px;
} }
} }
......
.white { .white {
background-color: #fff;
.line.hll { .line.hll {
background: #FFA; background: #FFA;
} }
.highlight{
border-left: 1px solid #eee;
}
pre { pre {
background-color: #fff; background-color: #fff;
color: #333; color: #333;
...@@ -179,8 +173,16 @@ ...@@ -179,8 +173,16 @@
@include box-shadow(0 5px 15px #000); @include box-shadow(0 5px 15px #000);
} }
.wiki, .note-body { .file-content {
.highlight { &.code .white {
border: 1px solid #DDD; .highlight {
border-left: 1px solid #eee;
}
}
&.wiki .white {
.highlight, pre, .hljs {
background: #F9F9F9;
}
} }
} }
...@@ -244,7 +244,6 @@ li.commit { ...@@ -244,7 +244,6 @@ li.commit {
font-family: inherit; font-family: inherit;
padding-left: $left; padding-left: $left;
position: relative; position: relative;
resize: vertical;
z-index: 2; z-index: 2;
} }
} }
...@@ -113,6 +113,10 @@ ...@@ -113,6 +113,10 @@
margin-bottom: 0; margin-bottom: 0;
} }
} }
.ci-coverage {
float: right;
}
} }
.merge-request-show-labels .label { .merge-request-show-labels .label {
......
...@@ -19,6 +19,10 @@ ul.notes { ...@@ -19,6 +19,10 @@ ul.notes {
@extend .cgray; @extend .cgray;
padding-bottom: 15px; padding-bottom: 15px;
a:hover {
text-decoration: none;
}
.avatar { .avatar {
float: left; float: left;
margin-right: 10px; margin-right: 10px;
...@@ -115,8 +119,7 @@ ul.notes { ...@@ -115,8 +119,7 @@ ul.notes {
display: none; display: none;
float: right; float: right;
[class^="icon-"], [class~="fa"] {
[class*="icon-"] {
font-size: 16px; font-size: 16px;
line-height: 16px; line-height: 16px;
vertical-align: middle; vertical-align: middle;
...@@ -143,8 +146,14 @@ ul.notes { ...@@ -143,8 +146,14 @@ ul.notes {
*/ */
.diff-file tr.line_holder { .diff-file tr.line_holder {
@mixin show-add-diff-note {
filter: alpha(opacity=100);
opacity: 1.0;
}
.add-diff-note { .add-diff-note {
background: image-url("diff_note_add.png") no-repeat left 0; background: image-url("diff_note_add.png") no-repeat left 0;
border: none;
height: 22px; height: 22px;
margin-left: -65px; margin-left: -65px;
position: absolute; position: absolute;
...@@ -156,8 +165,7 @@ ul.notes { ...@@ -156,8 +165,7 @@ ul.notes {
filter: alpha(opacity=0); filter: alpha(opacity=0);
&:hover { &:hover {
opacity: 1.0; @include show-add-diff-note;
filter: alpha(opacity=100);
} }
} }
...@@ -166,8 +174,7 @@ ul.notes { ...@@ -166,8 +174,7 @@ ul.notes {
background: $hover !important; background: $hover !important;
.add-diff-note { .add-diff-note {
opacity: 1.0; @include show-add-diff-note;
filter: alpha(opacity=100);
} }
} }
} }
......
...@@ -48,6 +48,10 @@ ...@@ -48,6 +48,10 @@
float: right; float: right;
margin-left: 20px; margin-left: 20px;
a:hover {
text-decoration: none;
}
.count { .count {
margin-left: 5px; margin-left: 5px;
} }
......
...@@ -8,7 +8,7 @@ class Admin::GroupsController < Admin::ApplicationController ...@@ -8,7 +8,7 @@ class Admin::GroupsController < Admin::ApplicationController
end end
def show def show
@members = @group.members.order("group_access DESC").page(params[:members_page]).per(30) @members = @group.members.order("access_level DESC").page(params[:members_page]).per(30)
@projects = @group.projects.page(params[:projects_page]).per(30) @projects = @group.projects.page(params[:projects_page]).per(30)
end end
...@@ -40,7 +40,7 @@ class Admin::GroupsController < Admin::ApplicationController ...@@ -40,7 +40,7 @@ class Admin::GroupsController < Admin::ApplicationController
end end
def project_teams_update def project_teams_update
@group.add_users(params[:user_ids].split(','), params[:group_access]) @group.add_users(params[:user_ids].split(','), params[:access_level])
redirect_to [:admin, @group], notice: 'Users were successfully added.' redirect_to [:admin, @group], notice: 'Users were successfully added.'
end end
......
...@@ -16,10 +16,10 @@ class Admin::ProjectsController < Admin::ApplicationController ...@@ -16,10 +16,10 @@ class Admin::ProjectsController < Admin::ApplicationController
def show def show
if @group if @group
@group_members = @group.members.order("group_access DESC").page(params[:group_members_page]).per(30) @group_members = @group.members.order("access_level DESC").page(params[:group_members_page]).per(30)
end end
@project_members = @project.users_projects.page(params[:project_members_page]).per(30) @project_members = @project.project_members.page(params[:project_members_page]).per(30)
end end
def transfer def transfer
......
...@@ -62,7 +62,7 @@ class ApplicationController < ActionController::Base ...@@ -62,7 +62,7 @@ class ApplicationController < ActionController::Base
end end
end end
def after_sign_in_path_for resource def after_sign_in_path_for(resource)
if resource.is_a?(User) && resource.respond_to?(:blocked?) && resource.blocked? if resource.is_a?(User) && resource.respond_to?(:blocked?) && resource.blocked?
sign_out resource sign_out resource
flash[:alert] = "Your account is blocked. Retry when an admin has unblocked it." flash[:alert] = "Your account is blocked. Retry when an admin has unblocked it."
......
class ConfirmationsController < Devise::ConfirmationsController
protected
def after_confirmation_path_for(resource_name, resource)
if signed_in?(resource_name)
signed_in_root_path(resource)
else
sign_in(resource)
if signed_in?(resource_name)
signed_in_root_path(resource)
else
new_session_path(resource_name)
end
end
end
end
class UsersGroupsController < ApplicationController class Groups::GroupMembersController < ApplicationController
before_filter :group before_filter :group
# Authorize # Authorize
...@@ -7,18 +7,18 @@ class UsersGroupsController < ApplicationController ...@@ -7,18 +7,18 @@ class UsersGroupsController < ApplicationController
layout 'group' layout 'group'
def create def create
@group.add_users(params[:user_ids].split(','), params[:group_access]) @group.add_users(params[:user_ids].split(','), params[:access_level])
redirect_to members_group_path(@group), notice: 'Users were successfully added.' redirect_to members_group_path(@group), notice: 'Users were successfully added.'
end end
def update def update
@member = @group.users_groups.find(params[:id]) @member = @group.group_members.find(params[:id])
@member.update_attributes(member_params) @member.update_attributes(member_params)
end end
def destroy def destroy
@users_group = @group.users_groups.find(params[:id]) @users_group = @group.group_members.find(params[:id])
if can?(current_user, :destroy, @users_group) # May fail if last owner. if can?(current_user, :destroy, @users_group) # May fail if last owner.
@users_group.destroy @users_group.destroy
respond_to do |format| respond_to do |format|
...@@ -43,6 +43,6 @@ class UsersGroupsController < ApplicationController ...@@ -43,6 +43,6 @@ class UsersGroupsController < ApplicationController
end end
def member_params def member_params
params.require(:users_group).permit(:group_access, :user_id) params.require(:group_member).permit(:access_level, :user_id)
end end
end end
...@@ -67,15 +67,15 @@ class GroupsController < ApplicationController ...@@ -67,15 +67,15 @@ class GroupsController < ApplicationController
def members def members
@project = group.projects.find(params[:project_id]) if params[:project_id] @project = group.projects.find(params[:project_id]) if params[:project_id]
@members = group.users_groups @members = group.group_members
if params[:search].present? if params[:search].present?
users = group.users.search(params[:search]).to_a users = group.users.search(params[:search]).to_a
@members = @members.where(user_id: users) @members = @members.where(user_id: users)
end end
@members = @members.order('group_access DESC').page(params[:page]).per(50) @members = @members.order('access_level DESC').page(params[:page]).per(50)
@users_group = UsersGroup.new @users_group = GroupMember.new
end end
def edit def edit
......
...@@ -2,11 +2,11 @@ class Profiles::GroupsController < ApplicationController ...@@ -2,11 +2,11 @@ class Profiles::GroupsController < ApplicationController
layout "profile" layout "profile"
def index def index
@user_groups = current_user.users_groups.page(params[:page]).per(20) @user_groups = current_user.group_members.page(params[:page]).per(20)
end end
def leave def leave
@users_group = group.users_groups.where(user_id: current_user.id).first @users_group = group.group_members.where(user_id: current_user.id).first
if can?(current_user, :destroy, @users_group) if can?(current_user, :destroy, @users_group)
@users_group.destroy @users_group.destroy
redirect_to(profile_groups_path, info: "You left #{group.name} group.") redirect_to(profile_groups_path, info: "You left #{group.name} group.")
......
...@@ -3,8 +3,8 @@ class Profiles::NotificationsController < ApplicationController ...@@ -3,8 +3,8 @@ class Profiles::NotificationsController < ApplicationController
def show def show
@notification = current_user.notification @notification = current_user.notification
@users_projects = current_user.users_projects @project_members = current_user.project_members
@users_groups = current_user.users_groups @group_members = current_user.group_members
end end
def update def update
...@@ -14,13 +14,13 @@ class Profiles::NotificationsController < ApplicationController ...@@ -14,13 +14,13 @@ class Profiles::NotificationsController < ApplicationController
current_user.notification_level = params[:notification_level] current_user.notification_level = params[:notification_level]
current_user.save current_user.save
elsif type == 'group' elsif type == 'group'
users_group = current_user.users_groups.find(params[:notification_id]) users_group = current_user.group_members.find(params[:notification_id])
users_group.notification_level = params[:notification_level] users_group.notification_level = params[:notification_level]
users_group.save users_group.save
else else
users_project = current_user.users_projects.find(params[:notification_id]) project_member = current_user.project_members.find(params[:notification_id])
users_project.notification_level = params[:notification_level] project_member.notification_level = params[:notification_level]
users_project.save project_member.save
end end
end end
end end
...@@ -17,10 +17,8 @@ class Projects::BranchesController < Projects::ApplicationController ...@@ -17,10 +17,8 @@ class Projects::BranchesController < Projects::ApplicationController
end end
def create def create
result = CreateBranchService.new.execute(project, result = CreateBranchService.new(project, current_user).
params[:branch_name], execute(params[:branch_name], params[:ref])
params[:ref],
current_user)
if result[:status] == :success if result[:status] == :success
@branch = result[:branch] @branch = result[:branch]
redirect_to project_tree_path(@project, @branch.name) redirect_to project_tree_path(@project, @branch.name)
...@@ -31,7 +29,7 @@ class Projects::BranchesController < Projects::ApplicationController ...@@ -31,7 +29,7 @@ class Projects::BranchesController < Projects::ApplicationController
end end
def destroy def destroy
DeleteBranchService.new.execute(project, params[:id], current_user) DeleteBranchService.new(project, current_user).execute(params[:id])
@branch_name = params[:id] @branch_name = params[:id]
respond_to do |format| respond_to do |format|
......
...@@ -12,20 +12,8 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -12,20 +12,8 @@ class Projects::CommitController < Projects::ApplicationController
return git_not_found! unless @commit return git_not_found! unless @commit
@line_notes = project.notes.for_commit_id(commit.id).inline @line_notes = project.notes.for_commit_id(commit.id).inline
@branches = project.repository.branch_names_contains(commit.id)
@branches = begin @diffs = @commit.diffs
project.repository.branch_names_contains(commit.id)
rescue Grit::Git::GitTimeout
[]
end
begin
@diffs = @commit.diffs
rescue Grit::Git::GitTimeout
@diffs = []
@diff_timeout = true
end
@note = project.build_commit_note(commit) @note = project.build_commit_note(commit)
@notes_count = project.notes.for_commit_id(commit.id).count @notes_count = project.notes.for_commit_id(commit.id).count
@notes = project.notes.for_commit_id(@commit.id).not_inline.fresh @notes = project.notes.for_commit_id(@commit.id).not_inline.fresh
......
...@@ -10,7 +10,8 @@ class Projects::EditTreeController < Projects::BaseTreeController ...@@ -10,7 +10,8 @@ class Projects::EditTreeController < Projects::BaseTreeController
end end
def update def update
result = Files::UpdateService.new(@project, current_user, params, @ref, @path).execute result = Files::UpdateService.
new(@project, current_user, params, @ref, @path).execute
if result[:status] == :success if result[:status] == :success
flash[:notice] = "Your changes have been successfully committed" flash[:notice] = "Your changes have been successfully committed"
......
...@@ -7,19 +7,34 @@ class Projects::GraphsController < Projects::ApplicationController ...@@ -7,19 +7,34 @@ class Projects::GraphsController < Projects::ApplicationController
def show def show
respond_to do |format| respond_to do |format|
format.html format.html
format.js do format.json do
fetch_graph fetch_graph
end end
end end
end end
def commits
@commits = @project.repository.commits(nil, nil, 2000, 0, true)
@commits_graph = Gitlab::Graphs::Commits.new(@commits)
@commits_per_week_days = @commits_graph.commits_per_week_days
@commits_per_time = @commits_graph.commits_per_time
@commits_per_month = @commits_graph.commits_per_month
end
private private
def fetch_graph def fetch_graph
@log = @project.repository.graph_log.to_json @commits = @project.repository.commits(nil, nil, 6000, 0, true)
@success = true
rescue => ex
@log = [] @log = []
@success = false
@commits.each do |commit|
@log << {
author_name: commit.author_name.force_encoding('UTF-8'),
author_email: commit.author_email.force_encoding('UTF-8'),
date: commit.committed_date.strftime("%Y-%m-%d")
}
end
render json: @log.to_json
end end
end end
...@@ -28,7 +28,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -28,7 +28,7 @@ class Projects::IssuesController < Projects::ApplicationController
@milestone = @project.milestones.find(milestone_id) if milestone_id.present? && !milestone_id.to_i.zero? @milestone = @project.milestones.find(milestone_id) if milestone_id.present? && !milestone_id.to_i.zero?
sort_param = params[:sort] || 'newest' sort_param = params[:sort] || 'newest'
@sort = sort_param.humanize unless sort_param.empty? @sort = sort_param.humanize unless sort_param.empty?
@assignees = User.where(id: @project.issues.pluck(:assignee_id)) @assignees = User.where(id: @project.issues.pluck(:assignee_id)).active
respond_to do |format| respond_to do |format|
format.html format.html
......
...@@ -122,7 +122,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -122,7 +122,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
if @merge_request.open? && @merge_request.can_be_merged? if @merge_request.open? && @merge_request.can_be_merged?
@merge_request.should_remove_source_branch = params[:should_remove_source_branch] @merge_request.should_remove_source_branch = params[:should_remove_source_branch]
@merge_request.automerge!(current_user, params[:merge_commit_message]) @merge_request.automerge!(current_user, params[:commit_message])
@status = true @status = true
else else
@status = false @status = false
...@@ -143,7 +143,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -143,7 +143,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def update_branches def update_branches
@target_project = selected_target_project @target_project = selected_target_project
@target_branches = @target_project.repository.branch_names @target_branches = @target_project.repository.branch_names
@target_branches
respond_to do |format| respond_to do |format|
format.js format.js
...@@ -151,8 +150,17 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -151,8 +150,17 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end end
def ci_status def ci_status
status = @merge_request.source_project.ci_service.commit_status(merge_request.last_commit.sha) ci_service = @merge_request.source_project.ci_service
response = {status: status} status = ci_service.commit_status(merge_request.last_commit.sha)
if ci_service.respond_to?(:commit_coverage)
coverage = ci_service.commit_coverage(merge_request.last_commit.sha)
end
response = {
status: status,
coverage: coverage
}
render json: response render json: response
end end
......
...@@ -13,7 +13,7 @@ class Projects::NewTreeController < Projects::BaseTreeController ...@@ -13,7 +13,7 @@ class Projects::NewTreeController < Projects::BaseTreeController
flash[:notice] = "Your changes have been successfully committed" flash[:notice] = "Your changes have been successfully committed"
redirect_to project_blob_path(@project, File.join(@ref, file_path)) redirect_to project_blob_path(@project, File.join(@ref, file_path))
else else
flash[:alert] = result[:error] flash[:alert] = result[:message]
render :show render :show
end end
end end
......
...@@ -4,11 +4,6 @@ class Projects::RepositoriesController < Projects::ApplicationController ...@@ -4,11 +4,6 @@ class Projects::RepositoriesController < Projects::ApplicationController
before_filter :authorize_code_access! before_filter :authorize_code_access!
before_filter :require_non_empty_project before_filter :require_non_empty_project
def stats
@stats = Gitlab::Git::Stats.new(@repository.raw, @repository.root_ref)
@graph = @stats.graph
end
def archive def archive
unless can?(current_user, :download_code, @project) unless can?(current_user, :download_code, @project)
render_404 and return render_404 and return
......
...@@ -13,9 +13,8 @@ class Projects::TagsController < Projects::ApplicationController ...@@ -13,9 +13,8 @@ class Projects::TagsController < Projects::ApplicationController
end end
def create def create
result = CreateTagService.new.execute(@project, params[:tag_name], result = CreateTagService.new(@project, current_user).
params[:ref], params[:message], execute(params[:tag_name], params[:ref], params[:message])
current_user)
if result[:status] == :success if result[:status] == :success
@tag = result[:tag] @tag = result[:tag]
redirect_to project_tags_path(@project) redirect_to project_tags_path(@project)
......
...@@ -6,18 +6,18 @@ class Projects::TeamMembersController < Projects::ApplicationController ...@@ -6,18 +6,18 @@ class Projects::TeamMembersController < Projects::ApplicationController
def index def index
@group = @project.group @group = @project.group
@users_projects = @project.users_projects.order('project_access DESC') @project_members = @project.project_members.order('access_level DESC')
@project_group_links = @project.project_group_links @project_group_links = @project.project_group_links
end end
def new def new
@user_project_relation = project.users_projects.new @user_project_relation = project.project_members.new
end end
def create def create
users = User.where(id: params[:user_ids].split(',')) users = User.where(id: params[:user_ids].split(','))
@project.team << [users, params[:project_access]] @project.team << [users, params[:access_level]]
if params[:redirect_to] if params[:redirect_to]
redirect_to params[:redirect_to] redirect_to params[:redirect_to]
...@@ -27,7 +27,7 @@ class Projects::TeamMembersController < Projects::ApplicationController ...@@ -27,7 +27,7 @@ class Projects::TeamMembersController < Projects::ApplicationController
end end
def update def update
@user_project_relation = project.users_projects.find_by(user_id: member) @user_project_relation = project.project_members.find_by(user_id: member)
@user_project_relation.update_attributes(member_params) @user_project_relation.update_attributes(member_params)
unless @user_project_relation.valid? unless @user_project_relation.valid?
...@@ -37,7 +37,7 @@ class Projects::TeamMembersController < Projects::ApplicationController ...@@ -37,7 +37,7 @@ class Projects::TeamMembersController < Projects::ApplicationController
end end
def destroy def destroy
@user_project_relation = project.users_projects.find_by(user_id: member) @user_project_relation = project.project_members.find_by(user_id: member)
@user_project_relation.destroy @user_project_relation.destroy
respond_to do |format| respond_to do |format|
...@@ -47,7 +47,7 @@ class Projects::TeamMembersController < Projects::ApplicationController ...@@ -47,7 +47,7 @@ class Projects::TeamMembersController < Projects::ApplicationController
end end
def leave def leave
project.users_projects.find_by(user_id: current_user).destroy project.project_members.find_by(user_id: current_user).destroy
respond_to do |format| respond_to do |format|
format.html { redirect_to :back } format.html { redirect_to :back }
...@@ -70,6 +70,6 @@ class Projects::TeamMembersController < Projects::ApplicationController ...@@ -70,6 +70,6 @@ class Projects::TeamMembersController < Projects::ApplicationController
end end
def member_params def member_params
params.require(:team_member).permit(:user_id, :project_access) params.require(:project_member).permit(:user_id, :access_level)
end end
end end
...@@ -15,11 +15,11 @@ class RegistrationsController < Devise::RegistrationsController ...@@ -15,11 +15,11 @@ class RegistrationsController < Devise::RegistrationsController
super super
end end
def after_sign_up_path_for resource def after_sign_up_path_for(resource)
new_user_session_path new_user_session_path
end end
def after_inactive_sign_up_path_for resource def after_inactive_sign_up_path_for(resource)
new_user_session_path new_user_session_path
end end
......
...@@ -19,10 +19,8 @@ class ProjectsFinder ...@@ -19,10 +19,8 @@ class ProjectsFinder
# Return ALL group projects # Return ALL group projects
group.projects group.projects
else else
projects_members = UsersProject.where( projects_members = ProjectMember.in_projects(group.projects).
project_id: group.projects, with_user(current_user)
user_id: current_user
)
if projects_members.any? if projects_members.any?
# User is a project member # User is a project member
...@@ -32,7 +30,11 @@ class ProjectsFinder ...@@ -32,7 +30,11 @@ class ProjectsFinder
# internal projects # internal projects
# joined projects # joined projects
# #
projects_ids = projects_members.pluck(:project_id) group.projects.where(
"projects.id IN (?) OR projects.visibility_level IN (?)",
projects_members.pluck(:source_id),
Project.public_and_internal_levels
)
else else
# User has no access to group or group projects # User has no access to group or group projects
# or has access through shared project # or has access through shared project
...@@ -41,20 +43,19 @@ class ProjectsFinder ...@@ -41,20 +43,19 @@ class ProjectsFinder
# public projects # public projects
# internal projects # internal projects
# shared projects # shared projects
projects_ids = [] projects_ids = []
ProjectGroupLink.where(project_id: group.projects).each do |shared_project| ProjectGroupLink.where(project_id: group.projects).each do |shared_project|
if shared_project.group.users.include?(current_user) || shared_project.project.users.include?(current_user) if shared_project.group.users.include?(current_user) || shared_project.project.users.include?(current_user)
projects_ids << shared_project.project.id projects_ids << shared_project.project.id
end end
end end
end
group.projects.where( group.projects.where(
"projects.id IN (?) OR projects.visibility_level IN (?)", "projects.id IN (?) OR projects.visibility_level IN (?)",
projects_ids, projects_ids,
Project.public_and_internal_levels Project.public_and_internal_levels
) )
end
end end
else else
# Not authenticated # Not authenticated
......
...@@ -151,12 +151,6 @@ module ApplicationHelper ...@@ -151,12 +151,6 @@ module ApplicationHelper
sanitize(str, tags: %w(a span)) sanitize(str, tags: %w(a span))
end end
def image_url(source)
# prevent relative_root_path being added twice (it's part of root_url and path_to_image)
root_url.sub(/#{root_path}$/, path_to_image(source))
end
alias_method :url_to_image, :image_url
def body_data_page def body_data_page
path = controller.controller_path.split('/') path = controller.controller_path.split('/')
...@@ -187,13 +181,6 @@ module ApplicationHelper ...@@ -187,13 +181,6 @@ module ApplicationHelper
end end
end end
def first_line(str)
lines = str.split("\n")
line = lines.first
line += "..." if lines.size > 1
line
end
def broadcast_message def broadcast_message
BroadcastMessage.current BroadcastMessage.current
end end
...@@ -242,7 +229,7 @@ module ApplicationHelper ...@@ -242,7 +229,7 @@ module ApplicationHelper
css_class << " hide" unless visible css_class << " hide" unless visible
content_tag :div, class: css_class do content_tag :div, class: css_class do
content_tag(:i, nil, class: 'icon-spinner icon-spin') + text content_tag(:i, nil, class: 'fa fa-spinner fa-spin') + text
end end
end end
......
...@@ -19,7 +19,7 @@ module EventsHelper ...@@ -19,7 +19,7 @@ module EventsHelper
[event.action_name, target].join(" ") [event.action_name, target].join(" ")
end end
def event_filter_link key, tooltip def event_filter_link(key, tooltip)
key = key.to_s key = key.to_s
inactive = if @event_filter.active? key inactive = if @event_filter.active? key
nil nil
...@@ -36,10 +36,10 @@ module EventsHelper ...@@ -36,10 +36,10 @@ module EventsHelper
def icon_for_event def icon_for_event
{ {
EventFilter.push => "icon-upload-alt", EventFilter.push => 'fa fa-upload',
EventFilter.merged => "icon-check", EventFilter.merged => 'fa fa-check-square-o',
EventFilter.comments => "icon-comments", EventFilter.comments => 'fa fa-comments',
EventFilter.team => "icon-user", EventFilter.team => 'fa fa-user',
} }
end end
...@@ -136,7 +136,7 @@ module EventsHelper ...@@ -136,7 +136,7 @@ module EventsHelper
end end
def event_note(text) def event_note(text)
text = first_line(text) text = first_line_in_markdown(text)
text = truncate(text, length: 150) text = truncate(text, length: 150)
sanitize(markdown(text), tags: %w(a img b pre p)) sanitize(markdown(text), tags: %w(a img b pre p))
end end
......
...@@ -51,6 +51,14 @@ module GitlabMarkdownHelper ...@@ -51,6 +51,14 @@ module GitlabMarkdownHelper
@markdown.render(text).html_safe @markdown.render(text).html_safe
end end
def first_line_in_markdown(text)
line = text.split("\n").detect do |i|
i.present? && markdown(i).present?
end
line += '...' unless line.nil?
line
end
def render_wiki_content(wiki_page) def render_wiki_content(wiki_page)
if wiki_page.format == :markdown if wiki_page.format == :markdown
markdown(wiki_page.content) markdown(wiki_page.content)
...@@ -65,7 +73,12 @@ module GitlabMarkdownHelper ...@@ -65,7 +73,12 @@ module GitlabMarkdownHelper
paths.uniq.each do |file_path| paths.uniq.each do |file_path|
# If project does not have repository # If project does not have repository
# its nothing to rebuild # its nothing to rebuild
if @repository.exists? && !@repository.empty? #
# TODO: pass project variable to markdown helper instead of using
# instance variable. Right now it generates invalid path for pages out
# of project scope. Example: search results where can be rendered markdown
# from different projects
if @repository && @repository.exists? && !@repository.empty?
new_path = rebuild_path(file_path) new_path = rebuild_path(file_path)
# Finds quoted path so we don't replace other mentions of the string # Finds quoted path so we don't replace other mentions of the string
# eg. "doc/api" will be replaced and "/home/doc/api/text" won't # eg. "doc/api" will be replaced and "/home/doc/api/text" won't
......
module IconsHelper module IconsHelper
def boolean_to_icon(value) def boolean_to_icon(value)
if value.to_s == "true" if value.to_s == "true"
content_tag :i, nil, class: 'icon-circle cgreen' content_tag :i, nil, class: 'fa fa-circle cgreen'
else else
content_tag :i, nil, class: 'icon-off clgray' content_tag :i, nil, class: 'fa fa-power-off clgray'
end end
end end
def public_icon def public_icon
content_tag :i, nil, class: 'icon-globe' content_tag :i, nil, class: 'fa fa-globe'
end end
def internal_icon def internal_icon
content_tag :i, nil, class: 'icon-shield' content_tag :i, nil, class: 'fa fa-shield'
end end
def private_icon def private_icon
content_tag :i, nil, class: 'icon-lock' content_tag :i, nil, class: 'fa fa-lock'
end end
end end
module IssuesHelper module IssuesHelper
def issue_css_classes issue def issue_css_classes(issue)
classes = "issue" classes = "issue"
classes << " closed" if issue.closed? classes << " closed" if issue.closed?
classes << " today" if issue.today? classes << " today" if issue.today?
...@@ -84,7 +84,7 @@ module IssuesHelper ...@@ -84,7 +84,7 @@ module IssuesHelper
'id', 'name', object.assignee_id) 'id', 'name', object.assignee_id)
end end
def milestone_options object def milestone_options(object)
options_from_collection_for_select(object.project.milestones.active, options_from_collection_for_select(object.project.milestones.active,
'id', 'title', object.milestone_id) 'id', 'title', object.milestone_id)
end end
......
...@@ -24,7 +24,7 @@ module MergeRequestsHelper ...@@ -24,7 +24,7 @@ module MergeRequestsHelper
} }
end end
def mr_css_classes mr def mr_css_classes(mr)
classes = "merge-request" classes = "merge-request"
classes << " closed" if mr.closed? classes << " closed" if mr.closed?
classes << " merged" if mr.merged? classes << " merged" if mr.merged?
......
...@@ -52,8 +52,8 @@ module NotesHelper ...@@ -52,8 +52,8 @@ module NotesHelper
discussion_id: discussion_id discussion_id: discussion_id
} }
link_to "", "javascript:;", class: "add-diff-note js-add-diff-note-button", button_tag '', class: 'btn add-diff-note js-add-diff-note-button',
data: data, title: "Add a comment to this line" data: data, title: 'Add a comment to this line'
end end
def link_to_reply_diff(note) def link_to_reply_diff(note)
...@@ -67,11 +67,10 @@ module NotesHelper ...@@ -67,11 +67,10 @@ module NotesHelper
discussion_id: note.discussion_id discussion_id: note.discussion_id
} }
link_to "javascript:;", class: "btn reply-btn js-discussion-reply-button", button_tag class: 'btn reply-btn js-discussion-reply-button',
data: data, title: "Add a reply" do data: data, title: 'Add a reply' do
link_text = "" link_text = content_tag(:i, nil, class: 'fa fa-comment')
link_text < content_tag(:i, nil, class: 'icon-comment') link_text << ' Reply'
link_text << "Reply" end
end
end end
end end
module NotificationsHelper module NotificationsHelper
def notification_icon(notification) def notification_icon(notification)
if notification.disabled? if notification.disabled?
content_tag :i, nil, class: 'icon-volume-off ns-mute' content_tag :i, nil, class: 'fa fa-volume-off ns-mute'
elsif notification.participating? elsif notification.participating?
content_tag :i, nil, class: 'icon-volume-down ns-part' content_tag :i, nil, class: 'fa fa-volume-down ns-part'
elsif notification.watch? elsif notification.watch?
content_tag :i, nil, class: 'icon-volume-up ns-watch' content_tag :i, nil, class: 'fa fa-volume-up ns-watch'
else else
content_tag :i, nil, class: 'icon-circle-blank ns-default' content_tag :i, nil, class: 'fa fa-circle-o ns-default'
end end
end end
end end
module ProfileHelper module ProfileHelper
def oauth_active_class provider def oauth_active_class(provider)
if current_user.provider == provider.to_s if current_user.provider == provider.to_s
'active' 'active'
end end
......
...@@ -3,7 +3,7 @@ module ProjectsHelper ...@@ -3,7 +3,7 @@ module ProjectsHelper
"You are going to remove #{user.name} from #{project.name} project team. Are you sure?" "You are going to remove #{user.name} from #{project.name} project team. Are you sure?"
end end
def link_to_project project def link_to_project(project)
link_to project do link_to project do
title = content_tag(:span, project.name, class: 'project-name') title = content_tag(:span, project.name, class: 'project-name')
...@@ -39,7 +39,7 @@ module ProjectsHelper ...@@ -39,7 +39,7 @@ module ProjectsHelper
end end
end end
def project_title project def project_title(project)
if project.group if project.group
content_tag :span do content_tag :span do
link_to(simple_sanitize(project.group.name), group_path(project.group)) + " / " + project.name link_to(simple_sanitize(project.group.name), group_path(project.group)) + " / " + project.name
...@@ -133,7 +133,7 @@ module ProjectsHelper ...@@ -133,7 +133,7 @@ module ProjectsHelper
'Star' 'Star'
end end
content_tag('i', ' ', class: 'icon-star') + toggle_text content_tag('i', ' ', class: 'fa fa-star') + toggle_text
end end
count_html = content_tag('span', class: 'count') do count_html = content_tag('span', class: 'count') do
...@@ -156,6 +156,14 @@ module ProjectsHelper ...@@ -156,6 +156,14 @@ module ProjectsHelper
end end
end end
def link_to_toggle_fork
out = content_tag(:i, '', class: 'fa fa-code-fork')
out << ' Fork'
out << content_tag(:span, class: 'count') do
@project.forks_count.to_s
end
end
private private
def get_project_nav_tabs(project, current_user) def get_project_nav_tabs(project, current_user)
......
...@@ -80,7 +80,8 @@ module SearchHelper ...@@ -80,7 +80,8 @@ module SearchHelper
# Autocomplete results for the current user's projects # Autocomplete results for the current user's projects
def projects_autocomplete(term, limit = 5) def projects_autocomplete(term, limit = 5)
ProjectsFinder.new.execute(current_user).search_by_title(term).non_archived.limit(limit).map do |p| ProjectsFinder.new.execute(current_user).search_by_title(term).
sorted_by_stars.non_archived.limit(limit).map do |p|
{ {
label: "project: #{search_result_sanitize(p.name_with_namespace)}", label: "project: #{search_result_sanitize(p.name_with_namespace)}",
url: project_path(p) url: project_path(p)
......
...@@ -2,6 +2,7 @@ module SelectsHelper ...@@ -2,6 +2,7 @@ module SelectsHelper
def users_select_tag(id, opts = {}) def users_select_tag(id, opts = {})
css_class = "ajax-users-select " css_class = "ajax-users-select "
css_class << "multiselect " if opts[:multiple] css_class << "multiselect " if opts[:multiple]
css_class << "skip_ldap " if opts[:skip_ldap]
css_class << (opts[:class] || '') css_class << (opts[:class] || '')
value = opts[:selected] || '' value = opts[:selected] || ''
......
...@@ -89,7 +89,7 @@ module TabHelper ...@@ -89,7 +89,7 @@ module TabHelper
end end
# Use nav_tab for save controller/action but different params # Use nav_tab for save controller/action but different params
def nav_tab key, value, &block def nav_tab(key, value, &block)
o = {} o = {}
o[:class] = "" o[:class] = ""
......
module TagsHelper module TagsHelper
def tag_path tag def tag_path(tag)
"/tags/#{tag}" "/tags/#{tag}"
end end
def tag_list project def tag_list(project)
html = '' html = ''
project.tag_list.each do |tag| project.tag_list.each do |tag|
html += link_to tag, tag_path(tag) html += link_to tag, tag_path(tag)
......
...@@ -36,9 +36,9 @@ module TreeHelper ...@@ -36,9 +36,9 @@ module TreeHelper
# type - String type of the tree item; either 'folder' or 'file' # type - String type of the tree item; either 'folder' or 'file'
def tree_icon(type) def tree_icon(type)
icon_class = if type == 'folder' icon_class = if type == 'folder'
'icon-folder-close' 'fa fa-folder'
else else
'icon-file-alt' 'fa fa-file-o'
end end
content_tag :i, nil, class: icon_class content_tag :i, nil, class: icon_class
...@@ -80,7 +80,7 @@ module TreeHelper ...@@ -80,7 +80,7 @@ module TreeHelper
end end
end end
def up_dir_path tree def up_dir_path(tree)
file = File.join(@path, "..") file = File.join(@path, "..")
tree_join(@ref, file) tree_join(@ref, file)
end end
......
module Emails module Emails
module Groups module Groups
def group_access_granted_email(user_group_id) def group_access_granted_email(user_group_id)
@membership = UsersGroup.find(user_group_id) @membership = GroupMember.find(user_group_id)
@group = @membership.group @group = @membership.group
@target_url = group_url(@group) @target_url = group_url(@group)
mail(to: @membership.user.email, mail(to: @membership.user.email,
......
module Emails module Emails
module Projects module Projects
def project_access_granted_email(user_project_id) def project_access_granted_email(user_project_id)
@users_project = UsersProject.find user_project_id @project_member = ProjectMember.find user_project_id
@project = @users_project.project @project = @project_member.project
@target_url = project_url(@project) @target_url = project_url(@project)
mail(to: @users_project.user.email, mail(to: @project_member.user.email,
subject: subject("Access to project was granted")) subject: subject("Access to project was granted"))
end end
......
...@@ -14,7 +14,7 @@ class Ability ...@@ -14,7 +14,7 @@ class Ability
when "MergeRequest" then merge_request_abilities(user, subject) when "MergeRequest" then merge_request_abilities(user, subject)
when "Group" then group_abilities(user, subject) when "Group" then group_abilities(user, subject)
when "Namespace" then namespace_abilities(user, subject) when "Namespace" then namespace_abilities(user, subject)
when "UsersGroup" then users_group_abilities(user, subject) when "GroupMember" then users_group_abilities(user, subject)
else [] else []
end.concat(global_abilities(user)) end.concat(global_abilities(user))
end end
...@@ -184,7 +184,7 @@ class Ability ...@@ -184,7 +184,7 @@ class Ability
] ]
end end
def group_abilities user, group def group_abilities(user, group)
rules = [] rules = []
if user.admin? || group.users.include?(user) || ProjectsFinder.new.execute(user, group: group).any? if user.admin? || group.users.include?(user) || ProjectsFinder.new.execute(user, group: group).any?
...@@ -209,7 +209,7 @@ class Ability ...@@ -209,7 +209,7 @@ class Ability
rules.flatten rules.flatten
end end
def namespace_abilities user, namespace def namespace_abilities(user, namespace)
rules = [] rules = []
# Only namespace owner and administrators can manage it # Only namespace owner and administrators can manage it
......
...@@ -88,9 +88,24 @@ class Commit ...@@ -88,9 +88,24 @@ class Commit
description.present? description.present?
end end
def hook_attrs(project)
path_with_namespace = project.path_with_namespace
{
id: id,
message: safe_message,
timestamp: committed_date.xmlschema,
url: "#{Gitlab.config.gitlab.url}/#{path_with_namespace}/commit/#{id}",
author: {
name: author_name,
email: author_email
}
}
end
# Discover issues should be closed when this commit is pushed to a project's # Discover issues should be closed when this commit is pushed to a project's
# default branch. # default branch.
def closes_issues project def closes_issues(project)
Gitlab::ClosingIssueExtractor.closed_by_message_in_project(safe_message, project) Gitlab::ClosingIssueExtractor.closed_by_message_in_project(safe_message, project)
end end
...@@ -108,4 +123,8 @@ class Commit ...@@ -108,4 +123,8 @@ class Commit
super super
end end
def parents
@parents ||= Commit.decorate(super)
end
end end
...@@ -134,7 +134,7 @@ module Issuable ...@@ -134,7 +134,7 @@ module Issuable
def to_hook_data def to_hook_data
{ {
object_kind: self.class.name.underscore, object_kind: self.class.name.underscore,
object_attributes: self.attributes object_attributes: hook_attrs
} }
end end
......
...@@ -10,7 +10,7 @@ module Mentionable ...@@ -10,7 +10,7 @@ module Mentionable
module ClassMethods module ClassMethods
# Indicate which attributes of the Mentionable to search for GFM references. # Indicate which attributes of the Mentionable to search for GFM references.
def attr_mentionable *attrs def attr_mentionable(*attrs)
mentionable_attrs.concat(attrs.map(&:to_s)) mentionable_attrs.concat(attrs.map(&:to_s))
end end
...@@ -38,7 +38,7 @@ module Mentionable ...@@ -38,7 +38,7 @@ module Mentionable
# Determine whether or not a cross-reference Note has already been created between this Mentionable and # Determine whether or not a cross-reference Note has already been created between this Mentionable and
# the specified target. # the specified target.
def has_mentioned? target def has_mentioned?(target)
Note.cross_reference_exists?(target, local_reference) Note.cross_reference_exists?(target, local_reference)
end end
...@@ -64,7 +64,7 @@ module Mentionable ...@@ -64,7 +64,7 @@ module Mentionable
end end
# Extract GFM references to other Mentionables from this Mentionable. Always excludes its #local_reference. # Extract GFM references to other Mentionables from this Mentionable. Always excludes its #local_reference.
def references p = project, text = mentionable_text def references(p = project, text = mentionable_text)
return [] if text.blank? return [] if text.blank?
ext = Gitlab::ReferenceExtractor.new ext = Gitlab::ReferenceExtractor.new
ext.analyze(text) ext.analyze(text)
...@@ -73,7 +73,7 @@ module Mentionable ...@@ -73,7 +73,7 @@ module Mentionable
end end
# Create a cross-reference Note for each GFM reference to another Mentionable found in +mentionable_text+. # Create a cross-reference Note for each GFM reference to another Mentionable found in +mentionable_text+.
def create_cross_references! p = project, a = author, without = [] def create_cross_references!(p = project, a = author, without = [])
refs = references(p) - without refs = references(p) - without
refs.each do |ref| refs.each do |ref|
Note.create_cross_reference_note(ref, local_reference, a, p) Note.create_cross_reference_note(ref, local_reference, a, p)
...@@ -82,7 +82,7 @@ module Mentionable ...@@ -82,7 +82,7 @@ module Mentionable
# If the mentionable_text field is about to change, locate any *added* references and create cross references for # If the mentionable_text field is about to change, locate any *added* references and create cross references for
# them. Invoke from an observer's #before_save implementation. # them. Invoke from an observer's #before_save implementation.
def notice_added_references p = project, a = author def notice_added_references(p = project, a = author)
ch = changed_attributes ch = changed_attributes
original, mentionable_changed = "", false original, mentionable_changed = "", false
self.class.mentionable_attrs.each do |attr| self.class.mentionable_attrs.each do |attr|
......
# == Notifiable concern # == Notifiable concern
# #
# Contains notification functionality shared between UsersProject and UsersGroup # Contains notification functionality
# #
module Notifiable module Notifiable
extend ActiveSupport::Concern extend ActiveSupport::Concern
......
...@@ -17,8 +17,8 @@ require 'carrierwave/orm/activerecord' ...@@ -17,8 +17,8 @@ require 'carrierwave/orm/activerecord'
require 'file_size_validator' require 'file_size_validator'
class Group < Namespace class Group < Namespace
has_many :users_groups, dependent: :destroy has_many :group_members, dependent: :destroy, as: :source, class_name: 'GroupMember'
has_many :users, through: :users_groups has_many :users, through: :group_members
has_many :project_group_links, dependent: :destroy has_many :project_group_links, dependent: :destroy
has_many :shared_projects, through: :project_group_links, source: :project has_many :shared_projects, through: :project_group_links, source: :project
has_many :ldap_group_links, foreign_key: 'group_id', dependent: :destroy has_many :ldap_group_links, foreign_key: 'group_id', dependent: :destroy
...@@ -33,22 +33,22 @@ class Group < Namespace ...@@ -33,22 +33,22 @@ class Group < Namespace
end end
def owners def owners
@owners ||= users_groups.owners.map(&:user) @owners ||= group_members.owners.map(&:user)
end end
def add_users(user_ids, group_access) def add_users(user_ids, access_level)
user_ids.compact.each do |user_id| user_ids.compact.each do |user_id|
user = self.users_groups.find_or_initialize_by(user_id: user_id) user = self.group_members.find_or_initialize_by(user_id: user_id)
user.update_attributes(group_access: group_access) user.update_attributes(access_level: access_level)
end end
end end
def add_user(user, group_access) def add_user(user, access_level)
self.users_groups.create(user_id: user.id, group_access: group_access) self.group_members.create(user_id: user.id, access_level: access_level)
end end
def add_owner(user) def add_owner(user)
self.add_user(user, UsersGroup::OWNER) self.add_user(user, Gitlab::Access::OWNER)
end end
def has_owner?(user) def has_owner?(user)
...@@ -64,7 +64,7 @@ class Group < Namespace ...@@ -64,7 +64,7 @@ class Group < Namespace
end end
def members def members
users_groups group_members
end end
def avatar_type def avatar_type
...@@ -90,6 +90,10 @@ class Group < Namespace ...@@ -90,6 +90,10 @@ class Group < Namespace
ldap_group_links.first.try(:group_access) ldap_group_links.first.try(:group_access)
end end
def ldap_synced?
ldap_cn.present?
end
class << self class << self
def search(query) def search(query)
where("LOWER(namespaces.name) LIKE :query", query: "%#{query.downcase}%") where("LOWER(namespaces.name) LIKE :query", query: "%#{query.downcase}%")
......
...@@ -21,6 +21,7 @@ class WebHook < ActiveRecord::Base ...@@ -21,6 +21,7 @@ class WebHook < ActiveRecord::Base
default_value_for :push_events, true default_value_for :push_events, true
default_value_for :issues_events, false default_value_for :issues_events, false
default_value_for :merge_requests_events, false default_value_for :merge_requests_events, false
default_value_for :tag_push_events, false
# HTTParty timeout # HTTParty timeout
default_timeout Gitlab.config.gitlab.webhook_timeout default_timeout Gitlab.config.gitlab.webhook_timeout
......
...@@ -48,6 +48,10 @@ class Issue < ActiveRecord::Base ...@@ -48,6 +48,10 @@ class Issue < ActiveRecord::Base
state :closed state :closed
end end
def hook_attrs
attributes
end
# Mentionable overrides. # Mentionable overrides.
def gfm_reference def gfm_reference
...@@ -65,4 +69,9 @@ class Issue < ActiveRecord::Base ...@@ -65,4 +69,9 @@ class Issue < ActiveRecord::Base
def reset_events_cache def reset_events_cache
Event.reset_event_cache_for(self) Event.reset_event_cache_for(self)
end end
# To allow polymorphism with MergeRequest.
def source_project
project
end
end end
...@@ -4,7 +4,7 @@ class LdapGroupLink < ActiveRecord::Base ...@@ -4,7 +4,7 @@ class LdapGroupLink < ActiveRecord::Base
validates :cn, :group_access, :group_id, presence: true validates :cn, :group_access, :group_id, presence: true
validates :cn, uniqueness: { scope: :group_id } validates :cn, uniqueness: { scope: :group_id }
validates :group_access, inclusion: { in: UsersGroup.group_access_roles.values } validates :group_access, inclusion: { in: Gitlab::Access.all_values }
def access_field def access_field
group_access group_access
......
class Member < ActiveRecord::Base
include Notifiable
include Gitlab::Access
belongs_to :user
belongs_to :source, polymorphic: true
validates :user, presence: true
validates :source, presence: true
validates :user_id, uniqueness: { scope: [:source_type, :source_id], message: "already exists in source" }
validates :access_level, inclusion: { in: Gitlab::Access.all_values }, presence: true
scope :guests, -> { where(access_level: GUEST) }
scope :reporters, -> { where(access_level: REPORTER) }
scope :developers, -> { where(access_level: DEVELOPER) }
scope :masters, -> { where(access_level: MASTER) }
scope :owners, -> { where(access_level: OWNER) }
delegate :name, :username, :email, to: :user, prefix: true
end
class GroupMember < Member
SOURCE_TYPE = 'Namespace'
belongs_to :group, class_name: 'Group', foreign_key: 'source_id'
# Make sure group member points only to group as it source
default_value_for :source_type, SOURCE_TYPE
default_value_for :notification_level, Notification::N_GLOBAL
validates_format_of :source_type, with: /\ANamespace\z/
default_scope { where(source_type: SOURCE_TYPE) }
scope :with_group, ->(group) { where(source_id: group.id) }
scope :with_user, ->(user) { where(user_id: user.id) }
scope :with_ldap_dn, -> { references(:user).includes(:user).
where(users: { provider: 'ldap' }) }
after_create :notify_create
after_update :notify_update
def self.access_level_roles
Gitlab::Access.options_with_owner
end
def group
source
end
def access_field
access_level
end
def notify_create
notification_service.new_group_member(self)
end
def notify_update
if access_level_changed?
notification_service.update_group_member(self)
end
end
def notification_service
NotificationService.new
end
end
# == Schema Information class ProjectMember < Member
# SOURCE_TYPE = 'Project'
# Table name: users_projects
#
# id :integer not null, primary key
# user_id :integer not null
# project_id :integer not null
# created_at :datetime
# updated_at :datetime
# project_access :integer default(0), not null
# notification_level :integer default(3), not null
#
class UsersProject < ActiveRecord::Base
include Gitlab::ShellAdapter
include Notifiable
include Gitlab::Access
belongs_to :user
belongs_to :project
validates :user, presence: true include Gitlab::ShellAdapter
validates :user_id, uniqueness: { scope: [:project_id], message: "already exists in project" }
validates :project_access, inclusion: { in: Gitlab::Access.values }, presence: true
validates :project, presence: true
delegate :name, :username, :email, to: :user, prefix: true belongs_to :project, class_name: 'Project', foreign_key: 'source_id'
scope :guests, -> { where(project_access: GUEST) }
scope :reporters, -> { where(project_access: REPORTER) }
scope :developers, -> { where(project_access: DEVELOPER) }
scope :masters, -> { where(project_access: MASTER) }
scope :in_project, ->(project) { where(project_id: project.id) } # Make sure project member points only to project as it source
scope :in_projects, ->(projects) { where(project_id: projects.map { |p| p.id }) } default_value_for :source_type, SOURCE_TYPE
scope :with_user, ->(user) { where(user_id: user.id) } default_value_for :notification_level, Notification::N_GLOBAL
validates_format_of :source_type, with: /\AProject\z/
default_scope { where(source_type: SOURCE_TYPE) }
after_create :post_create_hook after_create :post_create_hook
after_update :post_update_hook after_update :post_update_hook
after_destroy :post_destroy_hook after_destroy :post_destroy_hook
scope :in_project, ->(project) { where(source_id: project.id) }
scope :in_projects, ->(projects) { where(source_id: projects.pluck(:id)) }
scope :with_user, ->(user) { where(user_id: user.id) }
class << self class << self
# Add users to project teams with passed access option # Add users to project teams with passed access option
...@@ -50,7 +31,7 @@ class UsersProject < ActiveRecord::Base ...@@ -50,7 +31,7 @@ class UsersProject < ActiveRecord::Base
# add_users_into_projects( # add_users_into_projects(
# project_ids, # project_ids,
# user_ids, # user_ids,
# UsersProject::MASTER # ProjectMember::MASTER
# ) # )
# #
# add_users_into_projects( # add_users_into_projects(
...@@ -60,20 +41,20 @@ class UsersProject < ActiveRecord::Base ...@@ -60,20 +41,20 @@ class UsersProject < ActiveRecord::Base
# ) # )
# #
def add_users_into_projects(project_ids, user_ids, access) def add_users_into_projects(project_ids, user_ids, access)
project_access = if roles_hash.has_key?(access) access_level = if roles_hash.has_key?(access)
roles_hash[access] roles_hash[access]
elsif roles_hash.values.include?(access.to_i) elsif roles_hash.values.include?(access.to_i)
access access
else else
raise "Non valid access" raise "Non valid access"
end end
UsersProject.transaction do ProjectMember.transaction do
project_ids.each do |project_id| project_ids.each do |project_id|
user_ids.each do |user_id| user_ids.each do |user_id|
users_project = UsersProject.new(project_access: project_access, user_id: user_id) member = ProjectMember.new(access_level: access_level, user_id: user_id)
users_project.project_id = project_id member.source_id = project_id
users_project.save member.save
end end
end end
end end
...@@ -84,10 +65,10 @@ class UsersProject < ActiveRecord::Base ...@@ -84,10 +65,10 @@ class UsersProject < ActiveRecord::Base
end end
def truncate_teams(project_ids) def truncate_teams(project_ids)
UsersProject.transaction do ProjectMember.transaction do
users_projects = UsersProject.where(project_id: project_ids) members = ProjectMember.where(source_id: project_ids)
users_projects.each do |users_project| members.each do |member|
users_project.destroy member.destroy
end end
end end
...@@ -96,7 +77,7 @@ class UsersProject < ActiveRecord::Base ...@@ -96,7 +77,7 @@ class UsersProject < ActiveRecord::Base
false false
end end
def truncate_team project def truncate_team(project)
truncate_teams [project.id] truncate_teams [project.id]
end end
...@@ -110,7 +91,7 @@ class UsersProject < ActiveRecord::Base ...@@ -110,7 +91,7 @@ class UsersProject < ActiveRecord::Base
end end
def access_field def access_field
project_access access_level
end end
def owner? def owner?
...@@ -129,7 +110,7 @@ class UsersProject < ActiveRecord::Base ...@@ -129,7 +110,7 @@ class UsersProject < ActiveRecord::Base
end end
def post_update_hook def post_update_hook
notification_service.update_team_member(self) if self.project_access_changed? notification_service.update_team_member(self) if self.access_level_changed?
end end
def post_destroy_hook def post_destroy_hook
...@@ -149,4 +130,8 @@ class UsersProject < ActiveRecord::Base ...@@ -149,4 +130,8 @@ class UsersProject < ActiveRecord::Base
def system_hook_service def system_hook_service
SystemHooksService.new SystemHooksService.new
end end
def project
source
end
end end
...@@ -122,9 +122,11 @@ class MergeRequest < ActiveRecord::Base ...@@ -122,9 +122,11 @@ class MergeRequest < ActiveRecord::Base
if opened? || reopened? if opened? || reopened?
similar_mrs = self.target_project.merge_requests.where(source_branch: source_branch, target_branch: target_branch, source_project_id: source_project.id).opened similar_mrs = self.target_project.merge_requests.where(source_branch: source_branch, target_branch: target_branch, source_project_id: source_project.id).opened
similar_mrs = similar_mrs.where('id not in (?)', self.id) if self.id similar_mrs = similar_mrs.where('id not in (?)', self.id) if self.id
if similar_mrs.any? if similar_mrs.any?
errors.add :base, "Cannot Create: This merge request already exists: #{similar_mrs.pluck(:title)}" errors.add :validate_branches,
"Cannot Create: This merge request already exists: #{
similar_mrs.pluck(:title)
}"
end end
end end
end end
...@@ -140,7 +142,8 @@ class MergeRequest < ActiveRecord::Base ...@@ -140,7 +142,8 @@ class MergeRequest < ActiveRecord::Base
if source_project.forked_from?(target_project) if source_project.forked_from?(target_project)
true true
else else
errors.add :base, "Source project is not a fork of target project" errors.add :validate_fork,
'Source project is not a fork of target project'
end end
end end
end end
...@@ -208,6 +211,20 @@ class MergeRequest < ActiveRecord::Base ...@@ -208,6 +211,20 @@ class MergeRequest < ActiveRecord::Base
Gitlab::Satellite::MergeAction.new(current_user, self).format_patch Gitlab::Satellite::MergeAction.new(current_user, self).format_patch
end end
def hook_attrs
attrs = {
source: source_project.hook_attrs,
target: target_project.hook_attrs,
last_commit: nil
}
unless last_commit.nil?
attrs.merge!(last_commit: last_commit.hook_attrs(source_project))
end
attributes.merge!(attrs)
end
def for_fork? def for_fork?
target_project != source_project target_project != source_project
end end
......
...@@ -38,7 +38,7 @@ class Namespace < ActiveRecord::Base ...@@ -38,7 +38,7 @@ class Namespace < ActiveRecord::Base
scope :root, -> { where('type IS NULL') } scope :root, -> { where('type IS NULL') }
def self.search query def self.search(query)
where("name LIKE :query OR path LIKE :query", query: "%#{query}%") where("name LIKE :query OR path LIKE :query", query: "%#{query}%")
end end
......
...@@ -6,7 +6,7 @@ module Network ...@@ -6,7 +6,7 @@ module Network
@max_count ||= 650 @max_count ||= 650
end end
def initialize project, ref, commit, filter_ref def initialize(project, ref, commit, filter_ref)
@project = project @project = project
@ref = ref @ref = ref
@commit = commit @commit = commit
......
...@@ -83,8 +83,8 @@ class Project < ActiveRecord::Base ...@@ -83,8 +83,8 @@ class Project < ActiveRecord::Base
has_many :snippets, dependent: :destroy, class_name: "ProjectSnippet" has_many :snippets, dependent: :destroy, class_name: "ProjectSnippet"
has_many :hooks, dependent: :destroy, class_name: "ProjectHook" has_many :hooks, dependent: :destroy, class_name: "ProjectHook"
has_many :protected_branches, dependent: :destroy has_many :protected_branches, dependent: :destroy
has_many :users_projects, dependent: :destroy has_many :project_members, dependent: :destroy, as: :source, class_name: 'ProjectMember'
has_many :users, through: :users_projects has_many :users, through: :project_members
has_many :deploy_keys_projects, dependent: :destroy has_many :deploy_keys_projects, dependent: :destroy
has_many :deploy_keys, through: :deploy_keys_projects has_many :deploy_keys, through: :deploy_keys_projects
has_many :users_star_projects, dependent: :destroy has_many :users_star_projects, dependent: :destroy
...@@ -129,6 +129,7 @@ class Project < ActiveRecord::Base ...@@ -129,6 +129,7 @@ class Project < ActiveRecord::Base
scope :in_namespace, ->(namespace) { where(namespace_id: namespace.id) } scope :in_namespace, ->(namespace) { where(namespace_id: namespace.id) }
scope :in_group_namespace, -> { joins(:group) } scope :in_group_namespace, -> { joins(:group) }
scope :sorted_by_activity, -> { reorder("projects.last_activity_at 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 :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_only, -> { where(visibility_level: Project::PUBLIC) }
...@@ -345,7 +346,7 @@ class Project < ActiveRecord::Base ...@@ -345,7 +346,7 @@ class Project < ActiveRecord::Base
path path
end end
def items_for entity def items_for(entity)
case entity case entity
when 'issue' then when 'issue' then
issues issues
...@@ -368,12 +369,12 @@ class Project < ActiveRecord::Base ...@@ -368,12 +369,12 @@ class Project < ActiveRecord::Base
def team_member_by_name_or_email(name = nil, email = nil) 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
users_projects.where(user: user) if user project_members.where(user: user) if user
end end
# Get Team Member record by user id # Get Team Member record by user id
def team_member_by_id(user_id) def team_member_by_id(user_id)
users_projects.find_by(user_id: user_id) project_members.find_by(user_id: user_id)
end end
def name_with_namespace def name_with_namespace
...@@ -437,15 +438,19 @@ class Project < ActiveRecord::Base ...@@ -437,15 +438,19 @@ class Project < ActiveRecord::Base
end end
# Add comment about pushing new commits to merge requests # Add comment about pushing new commits to merge requests
mrs = self.merge_requests.opened.where(source_branch: branch_name).to_a comment_mr_with_commits(branch_name, commits, user)
true
end
def comment_mr_with_commits(branch_name, commits, user)
mrs = self.origin_merge_requests.opened.where(source_branch: branch_name).to_a
mrs += self.fork_merge_requests.opened.where(source_branch: branch_name).to_a mrs += self.fork_merge_requests.opened.where(source_branch: branch_name).to_a
mrs.uniq.each do |merge_request| mrs.uniq.each do |merge_request|
Note.create_new_commits_note(merge_request, merge_request.project, Note.create_new_commits_note(merge_request, merge_request.project,
user, commits) user, commits)
end end
true
end end
def valid_repo? def valid_repo?
...@@ -514,7 +519,7 @@ class Project < ActiveRecord::Base ...@@ -514,7 +519,7 @@ class Project < ActiveRecord::Base
end end
# Check if current branch name is marked as protected in the system # Check if current branch name is marked as protected in the system
def protected_branch? branch_name def protected_branch?(branch_name)
protected_branches_names.include?(branch_name) protected_branches_names.include?(branch_name)
end end
...@@ -554,6 +559,16 @@ class Project < ActiveRecord::Base ...@@ -554,6 +559,16 @@ class Project < ActiveRecord::Base
end end
end end
def hook_attrs
{
name: name,
ssh_url: ssh_url_to_repo,
http_url: http_url_to_repo,
namespace: namespace.name,
visibility_level: visibility_level
}
end
# Reset events cache related to this project # Reset events cache related to this project
# #
# Since we do cache @event we need to reset cache in special cases: # Since we do cache @event we need to reset cache in special cases:
...@@ -570,7 +585,7 @@ class Project < ActiveRecord::Base ...@@ -570,7 +585,7 @@ class Project < ActiveRecord::Base
end end
def project_member(user) def project_member(user)
users_projects.where(user_id: user).first project_members.where(user_id: user).first
end end
def default_branch def default_branch
...@@ -614,4 +629,16 @@ class Project < ActiveRecord::Base ...@@ -614,4 +629,16 @@ class Project < ActiveRecord::Base
def find_label(name) def find_label(name)
labels.find_by(name: name) labels.find_by(name: name)
end end
def origin_merge_requests
merge_requests.where(source_project_id: self.id)
end
def group_ldap_synced?
if group
group.ldap_synced?
else
false
end
end
end end
...@@ -27,12 +27,17 @@ class GitlabCiService < CiService ...@@ -27,12 +27,17 @@ class GitlabCiService < CiService
hook.save hook.save
end end
def commit_status_path sha def commit_status_path(sha)
project_url + "/builds/#{sha}/status.json?token=#{token}" project_url + "/builds/#{sha}/status.json?token=#{token}"
end end
def commit_status sha def get_ci_build(sha)
response = HTTParty.get(commit_status_path(sha), verify: false) @ci_builds ||= {}
@ci_builds[sha] ||= HTTParty.get(commit_status_path(sha), verify: false)
end
def commit_status(sha)
response = get_ci_build(sha)
if response.code == 200 and response["status"] if response.code == 200 and response["status"]
response["status"] response["status"]
...@@ -41,7 +46,15 @@ class GitlabCiService < CiService ...@@ -41,7 +46,15 @@ class GitlabCiService < CiService
end end
end end
def build_page sha def commit_coverage(sha)
response = get_ci_build(sha)
if response.code == 200 and response["coverage"]
response["coverage"]
end
end
def build_page(sha)
project_url + "/builds/#{sha}" project_url + "/builds/#{sha}"
end end
......
...@@ -11,7 +11,7 @@ class ProjectTeam ...@@ -11,7 +11,7 @@ class ProjectTeam
# @team << [@user, :master] # @team << [@user, :master]
# @team << [@users, :master] # @team << [@users, :master]
# #
def << args def <<(args)
users = args.first users = args.first
if users.respond_to?(:each) if users.respond_to?(:each)
...@@ -32,12 +32,12 @@ class ProjectTeam ...@@ -32,12 +32,12 @@ class ProjectTeam
end end
def find_tm(user_id) def find_tm(user_id)
tm = project.users_projects.find_by(user_id: user_id) tm = project.project_members.find_by(user_id: user_id)
# If user is not in project members # If user is not in project members
# we should check for group membership # we should check for group membership
if group && !tm if group && !tm
tm = group.users_groups.find_by(user_id: user_id) tm = group.group_members.find_by(user_id: user_id)
end end
tm tm
...@@ -52,7 +52,7 @@ class ProjectTeam ...@@ -52,7 +52,7 @@ class ProjectTeam
end end
def add_users_ids(user_ids, access) def add_users_ids(user_ids, access)
UsersProject.add_users_into_projects( ProjectMember.add_users_into_projects(
[project.id], [project.id],
user_ids, user_ids,
access access
...@@ -61,7 +61,7 @@ class ProjectTeam ...@@ -61,7 +61,7 @@ class ProjectTeam
# Remove all users from project team # Remove all users from project team
def truncate def truncate
UsersProject.truncate_team(project) ProjectMember.truncate_team(project)
end end
def users def users
...@@ -91,8 +91,8 @@ class ProjectTeam ...@@ -91,8 +91,8 @@ class ProjectTeam
def import(source_project) def import(source_project)
target_project = project target_project = project
source_team = source_project.users_projects.to_a source_team = source_project.project_members.to_a
target_user_ids = target_project.users_projects.pluck(:user_id) target_user_ids = target_project.project_members.pluck(:user_id)
source_team.reject! do |tm| source_team.reject! do |tm|
# Skip if user already present in team # Skip if user already present in team
...@@ -102,11 +102,11 @@ class ProjectTeam ...@@ -102,11 +102,11 @@ class ProjectTeam
source_team.map! do |tm| source_team.map! do |tm|
new_tm = tm.dup new_tm = tm.dup
new_tm.id = nil new_tm.id = nil
new_tm.project_id = target_project.id new_tm.source = target_project
new_tm new_tm
end end
UsersProject.transaction do ProjectMember.transaction do
source_team.each do |tm| source_team.each do |tm|
tm.save tm.save
end end
...@@ -135,10 +135,10 @@ class ProjectTeam ...@@ -135,10 +135,10 @@ class ProjectTeam
def max_tm_access(user_id) def max_tm_access(user_id)
access = [] access = []
access << project.users_projects.find_by(user_id: user_id).try(:access_field) access << project.project_members.find_by(user_id: user_id).try(:access_field)
if group if group
access << group.users_groups.find_by(user_id: user_id).try(:access_field) access << group.group_members.find_by(user_id: user_id).try(:access_field)
end end
if project.invited_groups.any? if project.invited_groups.any?
...@@ -152,7 +152,7 @@ class ProjectTeam ...@@ -152,7 +152,7 @@ class ProjectTeam
def max_invited_level(user_id) def max_invited_level(user_id)
project.project_group_links.map do |group_link| project.project_group_links.map do |group_link|
invited_group = group_link.group invited_group = group_link.group
access = invited_group.users_groups.find_by(user_id: user_id).try(:access_field) access = invited_group.group_members.find_by(user_id: user_id).try(:access_field)
# If group member has higher access level we should restrict it # If group member has higher access level we should restrict it
# to max allowed access level # to max allowed access level
...@@ -167,17 +167,17 @@ class ProjectTeam ...@@ -167,17 +167,17 @@ class ProjectTeam
private private
def fetch_members(level = nil) def fetch_members(level = nil)
project_members = project.users_projects project_members = project.project_members
group_members = group ? group.users_groups : [] group_members = group ? group.group_members : []
invited_members = [] invited_members = []
if project.invited_groups.any? if project.invited_groups.any?
project.project_group_links.each do |group_link| project.project_group_links.each do |group_link|
invited_group = group_link.group invited_group = group_link.group
im = invited_group.users_groups im = invited_group.group_members
if level if level
int_level = UsersGroup.group_access_roles[level.to_s.singularize.titleize] int_level = GroupMember.access_level_roles[level.to_s.singularize.titleize]
# Skip group members if we ask for masters # Skip group members if we ask for masters
# but max group access is developers # but max group access is developers
...@@ -187,7 +187,7 @@ class ProjectTeam ...@@ -187,7 +187,7 @@ class ProjectTeam
# group access is developers we need to provide # group access is developers we need to provide
# both group master, developers as devs # both group master, developers as devs
if int_level == group_link.group_access if int_level == group_link.group_access
im.where("group_access >= ?)", group_link.group_access) im.where("access_level >= ?)", group_link.group_access)
else else
im.send(level) im.send(level)
end end
......
...@@ -107,6 +107,18 @@ class ProjectWiki ...@@ -107,6 +107,18 @@ class ProjectWiki
[title.gsub(/\.[^.]*$/, ""), title_array.join("/")] [title.gsub(/\.[^.]*$/, ""), title_array.join("/")]
end end
def search_files(query)
repository.search_files(query, default_branch)
end
def repository
Repository.new(path_with_namespace, default_branch)
end
def default_branch
wiki.class.default_ref
end
private private
def create_repo! def create_repo!
......
...@@ -25,14 +25,14 @@ class Repository ...@@ -25,14 +25,14 @@ class Repository
raw_repository.empty? raw_repository.empty?
end end
def commit(id = nil) def commit(id = 'HEAD')
return nil unless raw_repository return nil unless raw_repository
commit = Gitlab::Git::Commit.find(raw_repository, id) commit = Gitlab::Git::Commit.find(raw_repository, id)
commit = Commit.new(commit) if commit commit = Commit.new(commit) if commit
commit commit
end end
def commits(ref, path = nil, limit = nil, offset = nil) def commits(ref, path = nil, limit = nil, offset = nil, skip_merges = false)
commits = Gitlab::Git::Commit.where( commits = Gitlab::Git::Commit.where(
repo: raw_repository, repo: raw_repository,
ref: ref, ref: ref,
...@@ -137,8 +137,18 @@ class Repository ...@@ -137,8 +137,18 @@ class Repository
def graph_log def graph_log
Rails.cache.fetch(cache_key(:graph_log)) do Rails.cache.fetch(cache_key(:graph_log)) do
stats = Gitlab::Git::GitStats.new(raw, root_ref, Gitlab.config.git.timeout) commits = raw_repository.log(limit: 6000, skip_merges: true,
stats.parsed_log ref: root_ref)
commits.map do |rugged_commit|
commit = Gitlab::Git::Commit.new(rugged_commit)
{
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
}
end
end end
end end
...@@ -223,12 +233,15 @@ class Repository ...@@ -223,12 +233,15 @@ class Repository
end end
def last_commit_for_path(sha, path) def last_commit_for_path(sha, path)
commits(sha, path, 1).last args = %W(git rev-list --max-count 1 #{sha} -- #{path})
sha = Gitlab::Popen.popen(args, path_to_repo).first.strip
commit(sha)
end end
# Remove archives older than 2 hours # Remove archives older than 2 hours
def clean_old_archives def clean_old_archives
Gitlab::Popen.popen(%W(find #{Gitlab.config.gitlab.repository_downloads_path} -mmin +120 -delete)) repository_downloads_path = Gitlab.config.gitlab.repository_downloads_path
Gitlab::Popen.popen(%W(find #{repository_downloads_path} -not -path #{repository_downloads_path} -mmin +120 -delete))
end end
def branches_sorted_by(value) def branches_sorted_by(value)
...@@ -247,20 +260,18 @@ class Repository ...@@ -247,20 +260,18 @@ class Repository
end end
def contributors def contributors
log = graph_log.group_by { |i| i[:author_email] } commits = self.commits(nil, nil, 2000, 0, true)
log.map do |email, contributions| commits.group_by(&:author_email).map do |email, commits|
contributor = Gitlab::Contributor.new contributor = Gitlab::Contributor.new
contributor.email = email contributor.email = email
contributions.each do |contribution| commits.each do |commit|
if contributor.name.blank? if contributor.name.blank?
contributor.name = contribution[:author_name] contributor.name = commit.author_name
end end
contributor.commits += 1 contributor.commits += 1
contributor.additions += contribution[:additions] || 0
contributor.deletions += contribution[:deletions] || 0
end end
contributor contributor
...@@ -282,4 +293,21 @@ class Repository ...@@ -282,4 +293,21 @@ class Repository
blob_at(commit.parent_id, diff.old_path) blob_at(commit.parent_id, diff.old_path)
end end
end end
def branch_names_contains(sha)
args = %W(git branch --contains #{sha})
names = Gitlab::Popen.popen(args, path_to_repo).first
if names.respond_to?(:split)
names = names.split("\n").map(&:strip)
names.each do |name|
name.slice! '* '
end
names
else
[]
end
end
end end
...@@ -81,21 +81,23 @@ class User < ActiveRecord::Base ...@@ -81,21 +81,23 @@ class User < ActiveRecord::Base
has_many :emails, dependent: :destroy has_many :emails, dependent: :destroy
# Groups # Groups
has_many :users_groups, dependent: :destroy has_many :members, dependent: :destroy
has_many :groups, through: :users_groups has_many :project_members, source: 'ProjectMember'
has_many :owned_groups, -> { where users_groups: { group_access: UsersGroup::OWNER } }, through: :users_groups, source: :group has_many :group_members, source: 'GroupMember'
has_many :masters_groups, -> { where users_groups: { group_access: UsersGroup::MASTER } }, through: :users_groups, source: :group has_many :groups, through: :group_members
has_many :owned_groups, -> { where members: { access_level: Gitlab::Access::OWNER } }, through: :group_members, source: :group
has_many :masters_groups, -> { where members: { access_level: Gitlab::Access::MASTER } }, through: :group_members, source: :group
# Projects # Projects
has_many :groups_projects, through: :groups, source: :projects has_many :groups_projects, through: :groups, source: :projects
has_many :personal_projects, through: :namespace, source: :projects has_many :personal_projects, through: :namespace, source: :projects
has_many :projects, through: :users_projects has_many :projects, through: :project_members
has_many :created_projects, foreign_key: :creator_id, class_name: 'Project' has_many :created_projects, foreign_key: :creator_id, class_name: 'Project'
has_many :users_star_projects, dependent: :destroy has_many :users_star_projects, dependent: :destroy
has_many :starred_projects, through: :users_star_projects, source: :project has_many :starred_projects, through: :users_star_projects, source: :project
has_many :snippets, dependent: :destroy, foreign_key: :author_id, class_name: "Snippet" has_many :snippets, dependent: :destroy, foreign_key: :author_id, class_name: "Snippet"
has_many :users_projects, dependent: :destroy has_many :project_members, dependent: :destroy, class_name: 'ProjectMember'
has_many :issues, dependent: :destroy, foreign_key: :author_id has_many :issues, dependent: :destroy, foreign_key: :author_id
has_many :notes, dependent: :destroy, foreign_key: :author_id has_many :notes, dependent: :destroy, foreign_key: :author_id
has_many :merge_requests, dependent: :destroy, foreign_key: :author_id has_many :merge_requests, dependent: :destroy, foreign_key: :author_id
...@@ -140,7 +142,7 @@ class User < ActiveRecord::Base ...@@ -140,7 +142,7 @@ class User < ActiveRecord::Base
state_machine :state, initial: :active do state_machine :state, initial: :active do
after_transition any => :blocked do |user, transition| after_transition any => :blocked do |user, transition|
# Remove user from all projects and # Remove user from all projects and
user.users_projects.find_each do |membership| user.project_members.find_each do |membership|
# skip owned resources # skip owned resources
next if membership.project.owner == user next if membership.project.owner == user
...@@ -148,7 +150,7 @@ class User < ActiveRecord::Base ...@@ -148,7 +150,7 @@ class User < ActiveRecord::Base
end end
# Remove user from all groups # Remove user from all groups
user.users_groups.find_each do |membership| user.group_members.find_each do |membership|
# skip owned resources # skip owned resources
next if membership.group.last_owner?(user) next if membership.group.last_owner?(user)
...@@ -175,7 +177,7 @@ class User < ActiveRecord::Base ...@@ -175,7 +177,7 @@ class User < ActiveRecord::Base
scope :in_team, ->(team){ where(id: team.member_ids) } scope :in_team, ->(team){ where(id: team.member_ids) }
scope :not_in_team, ->(team){ where('users.id NOT IN (:ids)', ids: team.member_ids) } scope :not_in_team, ->(team){ where('users.id NOT IN (:ids)', ids: team.member_ids) }
scope :not_in_project, ->(project) { project.users.present? ? where("id not in (:ids)", ids: project.users.map(&:id) ) : all } scope :not_in_project, ->(project) { project.users.present? ? where("id not in (:ids)", ids: project.users.map(&:id) ) : all }
scope :without_projects, -> { where('id NOT IN (SELECT DISTINCT(user_id) FROM users_projects)') } scope :without_projects, -> { where('id NOT IN (SELECT DISTINCT(user_id) FROM members)') }
scope :ldap, -> { where('provider LIKE ?', 'ldap%') } scope :ldap, -> { where('provider LIKE ?', 'ldap%') }
scope :subscribed_for_admin_email, -> { where(admin_email_unsubscribed_at: nil) } scope :subscribed_for_admin_email, -> { where(admin_email_unsubscribed_at: nil) }
...@@ -202,7 +204,7 @@ class User < ActiveRecord::Base ...@@ -202,7 +204,7 @@ class User < ActiveRecord::Base
User.where(name: name).first User.where(name: name).first
end end
def filter filter_name def filter(filter_name)
case filter_name case filter_name
when "admins"; self.admins when "admins"; self.admins
when "blocked"; self.blocked when "blocked"; self.blocked
...@@ -212,7 +214,7 @@ class User < ActiveRecord::Base ...@@ -212,7 +214,7 @@ class User < ActiveRecord::Base
end end
end end
def search query def search(query)
where("lower(name) LIKE :query OR lower(email) LIKE :query OR lower(username) LIKE :query", query: "%#{query.downcase}%") where("lower(name) LIKE :query OR lower(email) LIKE :query OR lower(username) LIKE :query", query: "%#{query.downcase}%")
end end
...@@ -297,7 +299,7 @@ class User < ActiveRecord::Base ...@@ -297,7 +299,7 @@ class User < ActiveRecord::Base
# Team membership in authorized projects # Team membership in authorized projects
def tm_in_authorized_projects def tm_in_authorized_projects
UsersProject.where(project_id: authorized_projects.map(&:id), user_id: self.id) ProjectMember.where(source_id: authorized_projects.map(&:id), user_id: self.id)
end end
def is_admin? def is_admin?
...@@ -332,7 +334,7 @@ class User < ActiveRecord::Base ...@@ -332,7 +334,7 @@ class User < ActiveRecord::Base
several_namespaces? || admin several_namespaces? || admin
end end
def can? action, subject def can?(action, subject)
abilities.allowed?(self, action, subject) abilities.allowed?(self, action, subject)
end end
...@@ -353,7 +355,7 @@ class User < ActiveRecord::Base ...@@ -353,7 +355,7 @@ class User < ActiveRecord::Base
(personal_projects.count.to_f / projects_limit) * 100 (personal_projects.count.to_f / projects_limit) * 100
end end
def recent_push project_id = nil def recent_push(project_id = nil)
# Get push events not earlier than 2 hours ago # Get push events not earlier than 2 hours ago
events = recent_events.code_push.where("created_at > ?", Time.now - 2.hours) events = recent_events.code_push.where("created_at > ?", Time.now - 2.hours)
events = events.where(project_id: project_id) if project_id events = events.where(project_id: project_id) if project_id
...@@ -382,11 +384,11 @@ class User < ActiveRecord::Base ...@@ -382,11 +384,11 @@ class User < ActiveRecord::Base
project.team_member_by_id(self.id) project.team_member_by_id(self.id)
end end
def already_forked? project def already_forked?(project)
!!fork_of(project) !!fork_of(project)
end end
def fork_of project def fork_of(project)
links = ForkedProjectLink.where(forked_from_project_id: project, forked_to_project_id: personal_projects) links = ForkedProjectLink.where(forked_from_project_id: project, forked_to_project_id: personal_projects)
if links.any? if links.any?
...@@ -512,7 +514,7 @@ class User < ActiveRecord::Base ...@@ -512,7 +514,7 @@ class User < ActiveRecord::Base
NotificationService.new NotificationService.new
end end
def log_info message def log_info(message)
Gitlab::AppLogger.info message Gitlab::AppLogger.info message
end end
......
# == Schema Information
#
# Table name: users_groups
#
# id :integer not null, primary key
# group_access :integer not null
# group_id :integer not null
# user_id :integer not null
# created_at :datetime
# updated_at :datetime
# notification_level :integer default(3), not null
#
class UsersGroup < ActiveRecord::Base
include Notifiable
include Gitlab::Access
def self.group_access_roles
Gitlab::Access.options_with_owner
end
belongs_to :user
belongs_to :group
scope :guests, -> { where(group_access: GUEST) }
scope :reporters, -> { where(group_access: REPORTER) }
scope :developers, -> { where(group_access: DEVELOPER) }
scope :masters, -> { where(group_access: MASTER) }
scope :owners, -> { where(group_access: OWNER) }
scope :with_ldap_dn, -> { references(:user).includes(:user).
where(users: { provider: 'ldap' }) }
scope :with_group, ->(group) { where(group_id: group.id) }
scope :with_user, ->(user) { where(user_id: user.id) }
after_create :notify_create
after_update :notify_update
validates :group_access, inclusion: { in: UsersGroup.group_access_roles.values }, presence: true
validates :user_id, presence: true
validates :group_id, presence: true
validates :user_id, uniqueness: { scope: [:group_id], message: "already exists in group" }
delegate :name, :username, :email, to: :user, prefix: true
def access_field
group_access
end
def notify_create
notification_service.new_group_member(self)
end
def notify_update
if group_access_changed?
notification_service.update_group_member(self)
end
end
def notification_service
NotificationService.new
end
end
...@@ -87,14 +87,14 @@ class WikiPage ...@@ -87,14 +87,14 @@ class WikiPage
def version def version
return nil unless persisted? return nil unless persisted?
@version ||= Commit.new(Gitlab::Git::Commit.new(@page.version)) @version ||= @page.version
end end
# Returns an array of Gitlab Commit instances. # Returns an array of Gitlab Commit instances.
def versions def versions
return [] unless persisted? return [] unless persisted?
@page.versions.map { |v| Commit.new(Gitlab::Git::Commit.new(v)) } @page.versions
end end
def commit def commit
......
class BaseService class BaseService
attr_accessor :project, :current_user, :params attr_accessor :project, :current_user, :params
def initialize(project, user, params) def initialize(project, user, params = {})
@project, @current_user, @params = project, user, params.dup @project, @current_user, @params = project, user, params.dup
end end
...@@ -25,11 +25,26 @@ class BaseService ...@@ -25,11 +25,26 @@ class BaseService
EventCreateService.new EventCreateService.new
end end
def log_info message def log_info(message)
Gitlab::AppLogger.info message Gitlab::AppLogger.info message
end end
def system_hook_service def system_hook_service
SystemHooksService.new SystemHooksService.new
end end
private
def error(message)
{
message: message,
status: :error
}
end
def success
{
status: :success
}
end
end end
class CreateBranchService require_relative 'base_service'
def execute(project, branch_name, ref, current_user)
class CreateBranchService < BaseService
def execute(branch_name, ref)
valid_branch = Gitlab::GitRefValidator.validate(branch_name) valid_branch = Gitlab::GitRefValidator.validate(branch_name)
if valid_branch == false if valid_branch == false
return error('Branch name invalid') return error('Branch name invalid')
...@@ -22,17 +24,9 @@ class CreateBranchService ...@@ -22,17 +24,9 @@ class CreateBranchService
end end
end end
def error(message)
{
message: message,
status: :error
}
end
def success(branch) def success(branch)
{ out = super()
branch: branch, out[:branch] = branch
status: :success out
}
end end
end end
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment