Commit a5972170 authored by Dmitriy Zaporozhets's avatar Dmitriy Zaporozhets

Merge remote-tracking branch 'origin/master' into ee-7-4

Signed-off-by: default avatarDmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com>

Conflicts:
	app/controllers/projects/team_members_controller.rb
	app/finders/projects_finder.rb
	app/models/group.rb
	app/models/project_team.rb
	app/models/users_group.rb
	config/gitlab.yml.example
	config/initializers/1_settings.rb
	config/routes.rb
	db/schema.rb
	doc/integration/ldap.md
	features/steps/group.rb
	features/steps/project/project.rb
parents cb51407c 9f939c96
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
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"
...@@ -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
......
...@@ -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)
...@@ -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)
...@@ -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)
...@@ -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)
......
...@@ -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
...@@ -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) ->
......
...@@ -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)
...@@ -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
......
...@@ -12,13 +12,13 @@ $(document).ready -> ...@@ -12,13 +12,13 @@ $(document).ready ->
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
...@@ -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
...@@ -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;
......
...@@ -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;
} }
} }
......
.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;
} }
} }
...@@ -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;
...@@ -143,8 +147,14 @@ ul.notes { ...@@ -143,8 +147,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 +166,7 @@ ul.notes { ...@@ -156,8 +166,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 +175,7 @@ ul.notes { ...@@ -166,8 +175,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
......
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
......
...@@ -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
......
...@@ -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
...@@ -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
......
...@@ -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
......
...@@ -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: 'icon-comment')
link_text < content_tag(:i, nil, class: 'icon-comment') link_text << ' Reply'
link_text << "Reply" end
end
end end
end end
...@@ -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: 'icon-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)
......
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
......
...@@ -108,4 +108,8 @@ class Commit ...@@ -108,4 +108,8 @@ class Commit
super super
end end
def parents
@parents ||= Commit.decorate(super)
end
end end
# == 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
......
...@@ -65,4 +65,9 @@ class Issue < ActiveRecord::Base ...@@ -65,4 +65,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
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
...@@ -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
......
...@@ -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) }
...@@ -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?
...@@ -570,7 +575,7 @@ class Project < ActiveRecord::Base ...@@ -570,7 +575,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 +619,8 @@ class Project < ActiveRecord::Base ...@@ -614,4 +619,8 @@ 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
end end
...@@ -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?
...@@ -167,8 +167,8 @@ class ProjectTeam ...@@ -167,8 +167,8 @@ 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?
......
...@@ -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: 'ldap') } scope :ldap, -> { where(provider: 'ldap') }
scope :subscribed_for_admin_email, -> { where(admin_email_unsubscribed_at: nil) } scope :subscribed_for_admin_email, -> { where(admin_email_unsubscribed_at: nil) }
...@@ -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?
......
# == 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
...@@ -32,4 +32,19 @@ class BaseService ...@@ -32,4 +32,19 @@ class BaseService
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
class CreateTagService require_relative 'base_service'
def execute(project, tag_name, ref, message, current_user)
class CreateTagService < BaseService
def execute(tag_name, ref, message)
valid_tag = Gitlab::GitRefValidator.validate(tag_name) valid_tag = Gitlab::GitRefValidator.validate(tag_name)
if valid_tag == false if valid_tag == false
return error('Tag name invalid') return error('Tag name invalid')
...@@ -26,17 +28,9 @@ class CreateTagService ...@@ -26,17 +28,9 @@ class CreateTagService
end end
end end
def error(message)
{
message: message,
status: :error
}
end
def success(branch) def success(branch)
{ out = super()
tag: branch, out[:tag] = branch
status: :success out
}
end end
end end
class DeleteBranchService require_relative 'base_service'
def execute(project, branch_name, current_user)
class DeleteBranchService < BaseService
def execute(branch_name)
repository = project.repository repository = project.repository
branch = repository.find_branch(branch_name) branch = repository.find_branch(branch_name)
...@@ -31,17 +33,14 @@ class DeleteBranchService ...@@ -31,17 +33,14 @@ class DeleteBranchService
end end
def error(message, return_code = 400) def error(message, return_code = 400)
{ out = super(message)
message: message, out[:return_code] = return_code
return_code: return_code, out
state: :error
}
end end
def success(message) def success(message)
{ out = super()
message: message, out[:message] = message
state: :success out
}
end end
end end
...@@ -10,18 +10,10 @@ module Files ...@@ -10,18 +10,10 @@ module Files
private private
def error(message)
{
error: message,
status: :error
}
end
def success def success
{ out = super()
error: '', out[:error] = ''
status: :success out
}
end end
def repository def repository
......
...@@ -17,39 +17,38 @@ class GitPushService ...@@ -17,39 +17,38 @@ class GitPushService
def execute(project, user, oldrev, newrev, ref) def execute(project, user, oldrev, newrev, ref)
@project, @user = project, user @project, @user = project, user
# Collect data for this git push
@push_commits = project.repository.commits_between(oldrev, newrev)
@push_data = post_receive_data(oldrev, newrev, ref)
create_push_event
project.ensure_satellite_exists project.ensure_satellite_exists
project.repository.expire_cache project.repository.expire_cache
project.update_repository_size project.update_repository_size
if push_to_existing_branch?(ref, oldrev)
project.update_merge_requests(oldrev, newrev, ref, @user)
process_commit_messages(ref)
end
if push_to_branch?(ref) if push_to_branch?(ref)
project.execute_hooks(@push_data.dup, :push_hooks) if push_remove_branch?(ref, newrev)
project.execute_services(@push_data.dup) @push_commits = []
end elsif push_to_new_branch?(ref, oldrev)
# Re-find the pushed commits.
if push_to_new_branch?(ref, oldrev) if is_default_branch?(ref)
# Re-find the pushed commits. # Initial push to the default branch. Take the full history of that branch as "newly pushed".
if is_default_branch?(ref) @push_commits = project.repository.commits(newrev)
# Initial push to the default branch. Take the full history of that branch as "newly pushed". # Default branch is protected by default
@push_commits = project.repository.commits(newrev) project.protected_branches.create({ name: project.default_branch })
else else
# Use the pushed commits that aren't reachable by the default branch # Use the pushed commits that aren't reachable by the default branch
# as a heuristic. This may include more commits than are actually pushed, but # as a heuristic. This may include more commits than are actually pushed, but
# that shouldn't matter because we check for existing cross-references later. # that shouldn't matter because we check for existing cross-references later.
@push_commits = project.repository.commits_between(project.default_branch, newrev) @push_commits = project.repository.commits_between(project.default_branch, newrev)
end
process_commit_messages(ref)
elsif push_to_existing_branch?(ref, oldrev)
# Collect data for this git push
@push_commits = project.repository.commits_between(oldrev, newrev)
project.update_merge_requests(oldrev, newrev, ref, @user)
process_commit_messages(ref)
end end
process_commit_messages(ref) @push_data = post_receive_data(oldrev, newrev, ref)
create_push_event(@push_data)
project.execute_hooks(@push_data.dup, :push_hooks)
project.execute_services(@push_data.dup)
end end
end end
...@@ -65,7 +64,7 @@ class GitPushService ...@@ -65,7 +64,7 @@ class GitPushService
protected protected
def create_push_event def create_push_event(push_data)
Event.create!( Event.create!(
project: project, project: project,
action: Event::PUSHED, action: Event::PUSHED,
...@@ -183,6 +182,12 @@ class GitPushService ...@@ -183,6 +182,12 @@ class GitPushService
ref_parts[1] =~ /heads/ && oldrev == "0000000000000000000000000000000000000000" ref_parts[1] =~ /heads/ && oldrev == "0000000000000000000000000000000000000000"
end end
def push_remove_branch? ref, newrev
ref_parts = ref.split('/')
ref_parts[1] =~ /heads/ && newrev == "0000000000000000000000000000000000000000"
end
def push_to_branch? ref def push_to_branch? ref
ref =~ /refs\/heads/ ref =~ /refs\/heads/
end end
......
...@@ -157,12 +157,12 @@ class NotificationService ...@@ -157,12 +157,12 @@ class NotificationService
end end
end end
def new_team_member(users_project) def new_team_member(project_member)
mailer.project_access_granted_email(users_project.id) mailer.project_access_granted_email(project_member.id)
end end
def update_team_member(users_project) def update_team_member(project_member)
mailer.project_access_granted_email(users_project.id) mailer.project_access_granted_email(project_member.id)
end end
def new_group_member(users_group) def new_group_member(users_group)
...@@ -186,20 +186,20 @@ class NotificationService ...@@ -186,20 +186,20 @@ class NotificationService
# Get project users with WATCH notification level # Get project users with WATCH notification level
def project_watchers(project) def project_watchers(project)
project_members = users_project_notification(project) project_members = project_member_notification(project)
users_with_project_level_global = users_project_notification(project, Notification::N_GLOBAL) users_with_project_level_global = project_member_notification(project, Notification::N_GLOBAL)
users_with_group_level_global = users_group_notification(project, Notification::N_GLOBAL) users_with_group_level_global = users_group_notification(project, Notification::N_GLOBAL)
users = users_with_global_level_watch([users_with_project_level_global, users_with_group_level_global].flatten.uniq) users = users_with_global_level_watch([users_with_project_level_global, users_with_group_level_global].flatten.uniq)
users_with_project_setting = select_users_project_setting(project, users_with_project_level_global, users) users_with_project_setting = select_project_member_setting(project, users_with_project_level_global, users)
users_with_group_setting = select_users_group_setting(project, project_members, users_with_group_level_global, users) users_with_group_setting = select_users_group_setting(project, project_members, users_with_group_level_global, users)
User.where(id: users_with_project_setting.concat(users_with_group_setting).uniq).to_a User.where(id: users_with_project_setting.concat(users_with_group_setting).uniq).to_a
end end
def users_project_notification(project, notification_level=nil) def project_member_notification(project, notification_level=nil)
project_members = project.users_projects project_members = project.project_members
if notification_level if notification_level
project_members.where(notification_level: notification_level).pluck(:user_id) project_members.where(notification_level: notification_level).pluck(:user_id)
...@@ -210,7 +210,7 @@ class NotificationService ...@@ -210,7 +210,7 @@ class NotificationService
def users_group_notification(project, notification_level) def users_group_notification(project, notification_level)
if project.group if project.group
project.group.users_groups.where(notification_level: notification_level).pluck(:user_id) project.group.group_members.where(notification_level: notification_level).pluck(:user_id)
else else
[] []
end end
...@@ -224,8 +224,8 @@ class NotificationService ...@@ -224,8 +224,8 @@ class NotificationService
end end
# Build a list of users based on project notifcation settings # Build a list of users based on project notifcation settings
def select_users_project_setting(project, global_setting, users_global_level_watch) def select_project_member_setting(project, global_setting, users_global_level_watch)
users = users_project_notification(project, Notification::N_WATCH) users = project_member_notification(project, Notification::N_WATCH)
# If project setting is global, add to watch list if global setting is watch # If project setting is global, add to watch list if global setting is watch
global_setting.each do |user_id| global_setting.each do |user_id|
...@@ -267,10 +267,10 @@ class NotificationService ...@@ -267,10 +267,10 @@ class NotificationService
users.reject do |user| users.reject do |user|
next user.notification.disabled? unless project next user.notification.disabled? unless project
tm = project.users_projects.find_by(user_id: user.id) tm = project.project_members.find_by(user_id: user.id)
if !tm && project.group if !tm && project.group
tm = project.group.users_groups.find_by(user_id: user.id) tm = project.group.group_members.find_by(user_id: user.id)
end end
# reject users who globally disabled notification and has no membership # reject users who globally disabled notification and has no membership
......
...@@ -42,10 +42,7 @@ module Projects ...@@ -42,10 +42,7 @@ module Projects
system_hook_service.execute_hooks_for(@project, :create) system_hook_service.execute_hooks_for(@project, :create)
unless @project.group unless @project.group
@project.users_projects.create( @project.team << [current_user, :master]
project_access: UsersProject::MASTER,
user: current_user
)
end end
@project.update_column(:last_activity_at, @project.created_at) @project.update_column(:last_activity_at, @project.created_at)
......
...@@ -27,7 +27,7 @@ module Projects ...@@ -27,7 +27,7 @@ module Projects
#First save the DB entries as they can be rolled back if the repo fork fails #First save the DB entries as they can be rolled back if the repo fork fails
project.build_forked_project_link(forked_to_project_id: project.id, forked_from_project_id: @from_project.id) project.build_forked_project_link(forked_to_project_id: project.id, forked_from_project_id: @from_project.id)
if project.save if project.save
project.users_projects.create(project_access: UsersProject::MASTER, user: current_user) project.team << [current_user, :master]
end end
#Now fork the repo #Now fork the repo
unless gitlab_shell.fork_repository(@from_project.path_with_namespace, project.namespace.path) unless gitlab_shell.fork_repository(@from_project.path_with_namespace, project.namespace.path)
......
...@@ -50,14 +50,14 @@ class SystemHooksService ...@@ -50,14 +50,14 @@ class SystemHooksService
email: model.email, email: model.email,
user_id: model.id user_id: model.id
}) })
when UsersProject when ProjectMember
data.merge!({ data.merge!({
project_name: model.project.name, project_name: model.project.name,
project_path: model.project.path, project_path: model.project.path,
project_id: model.project_id, project_id: model.project.id,
user_name: model.user.name, user_name: model.user.name,
user_email: model.user.email, user_email: model.user.email,
project_access: model.human_access, access_level: model.human_access,
project_visibility: Project.visibility_levels.key(model.project.visibility_level_field).downcase project_visibility: Project.visibility_levels.key(model.project.visibility_level_field).downcase
}) })
end end
...@@ -65,7 +65,7 @@ class SystemHooksService ...@@ -65,7 +65,7 @@ class SystemHooksService
def build_event_name(model, event) def build_event_name(model, event)
case model case model
when UsersProject when ProjectMember
return "user_add_to_team" if event == :create return "user_add_to_team" if event == :create
return "user_remove_from_team" if event == :destroy return "user_remove_from_team" if event == :destroy
else else
......
...@@ -89,7 +89,7 @@ ...@@ -89,7 +89,7 @@
%div %div
= users_select_tag(:user_ids, multiple: true) = users_select_tag(:user_ids, multiple: true)
%div.prepend-top-10 %div.prepend-top-10
= select_tag :group_access, options_for_select(UsersGroup.group_access_roles), class: "project-access-select select2" = select_tag :access_level, options_for_select(GroupMember.access_level_roles), class: "project-access-select select2"
%hr %hr
= submit_tag 'Add users into group', class: "btn btn-create" = submit_tag 'Add users into group', class: "btn btn-create"
.panel.panel-default .panel.panel-default
...@@ -97,7 +97,7 @@ ...@@ -97,7 +97,7 @@
%h3.panel-title %h3.panel-title
Members Members
%span.badge %span.badge
#{@group.users_groups.count} #{@group.group_members.count}
%ul.well-list.group-users-list %ul.well-list.group-users-list
- @members.each do |member| - @members.each do |member|
- user = member.user - user = member.user
...@@ -107,7 +107,7 @@ ...@@ -107,7 +107,7 @@
= link_to user.name, admin_user_path(user) = link_to user.name, admin_user_path(user)
%span.pull-right.light %span.pull-right.light
= member.human_access = member.human_access
= link_to group_users_group_path(@group, member), data: { confirm: remove_user_from_group_message(@group, user) }, method: :delete, remote: true, class: "btn-tiny btn btn-remove", title: 'Remove user from group' do = link_to group_group_members_path(@group, member), data: { confirm: remove_user_from_group_message(@group, user) }, method: :delete, remote: true, class: "btn-tiny btn btn-remove", title: 'Remove user from group' do
%i.icon-minus.icon-white %i.icon-minus.icon-white
.panel-footer .panel-footer
= paginate @members, param_name: 'members_page', theme: 'gitlab' = paginate @members, param_name: 'members_page', theme: 'gitlab'
...@@ -95,13 +95,13 @@ ...@@ -95,13 +95,13 @@
.panel.panel-default .panel.panel-default
.panel-heading .panel-heading
%strong #{@group.name} %strong #{@group.name}
group members (#{@group.users_groups.count}) group members (#{@group.group_members.count})
.pull-right .pull-right
= link_to admin_group_path(@group), class: 'btn btn-small' do = link_to admin_group_path(@group), class: 'btn btn-small' do
%i.icon-edit %i.icon-edit
%ul.well-list %ul.well-list
- @group_members.each do |member| - @group_members.each do |member|
= render 'users_groups/users_group', member: member, show_controls: false = render 'groups/group_members/group_member', member: member, show_controls: false
.panel-footer .panel-footer
= paginate @group_members, param_name: 'group_members_page', theme: 'gitlab' = paginate @group_members, param_name: 'group_members_page', theme: 'gitlab'
...@@ -115,17 +115,17 @@ ...@@ -115,17 +115,17 @@
%i.icon-edit %i.icon-edit
Manage Access Manage Access
%ul.well-list.team_members %ul.well-list.team_members
- @project_members.each do |users_project| - @project_members.each do |project_member|
- user = users_project.user - user = project_member.user
%li.users_project %li.project_member
.list-item-name .list-item-name
%strong %strong
= link_to user.name, admin_user_path(user) = link_to user.name, admin_user_path(user)
.pull-right .pull-right
- if users_project.owner? - if project_member.owner?
%span.light Owner %span.light Owner
- else - else
%span.light= users_project.human_access %span.light= project_member.human_access
= link_to project_team_member_path(@project, user), data: { confirm: remove_from_project_team_message(@project, user)}, method: :delete, remote: true, class: "btn btn-small btn-remove" do = link_to project_team_member_path(@project, user), data: { confirm: remove_from_project_team_message(@project, user)}, method: :delete, remote: true, class: "btn btn-small btn-remove" do
%i.icon-remove %i.icon-remove
.panel-footer .panel-footer
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
= form_tag admin_users_path, method: :get, class: 'form-inline' do = form_tag admin_users_path, method: :get, class: 'form-inline' do
.form-group .form-group
= search_field_tag :name, params[:name], placeholder: 'Name, email or username', class: 'form-control' = search_field_tag :name, params[:name], placeholder: 'Name, email or username', class: 'form-control'
= button_tag type: 'submit', class: 'btn btn-primary' do = button_tag class: 'btn btn-primary' do
%i.icon-search %i.icon-search
%hr %hr
= link_to 'Reset', admin_users_path, class: "btn btn-cancel" = link_to 'Reset', admin_users_path, class: "btn btn-cancel"
......
...@@ -159,13 +159,13 @@ ...@@ -159,13 +159,13 @@
= render 'users/profile', user: @user = render 'users/profile', user: @user
#groups.tab-pane #groups.tab-pane
- if @user.users_groups.present? - if @user.group_members.present?
.panel.panel-default .panel.panel-default
.panel-heading Groups: .panel-heading Groups:
%ul.well-list %ul.well-list
- @user.users_groups.each do |user_group| - @user.group_members.each do |user_group|
- group = user_group.group - group = user_group.group
%li.users_group %li.group_member
%span{class: ("list-item-name" unless user_group.owner?)} %span{class: ("list-item-name" unless user_group.owner?)}
%strong= link_to group.name, admin_group_path(group) %strong= link_to group.name, admin_group_path(group)
.pull-right .pull-right
...@@ -197,7 +197,7 @@ ...@@ -197,7 +197,7 @@
%ul.well-list %ul.well-list
- @joined_projects.sort_by(&:name_with_namespace).each do |project| - @joined_projects.sort_by(&:name_with_namespace).each do |project|
- tm = project.team.find_tm(@user.id) - tm = project.team.find_tm(@user.id)
%li.users_project %li.project_member
.list-item-name .list-item-name
= link_to admin_project_path(project), class: dom_class(project) do = link_to admin_project_path(project), class: dom_class(project) do
= project.name_with_namespace = project.name_with_namespace
......
%div{xmlns: "http://www.w3.org/1999/xhtml"} %div{xmlns: "http://www.w3.org/1999/xhtml"}
= markdown issue.description - if issue.description.present?
= markdown issue.description
%div{xmlns: "http://www.w3.org/1999/xhtml"} %div{xmlns: "http://www.w3.org/1999/xhtml"}
= markdown merge_request.description - if merge_request.description.present?
= markdown merge_request.description
= form_for @users_group, url: group_users_groups_path(@group), html: { class: 'form-horizontal users-group-form' } do |f| = form_for @users_group, url: group_group_members_path(@group), html: { class: 'form-horizontal users-group-form' } do |f|
.form-group .form-group
= f.label :user_ids, "People", class: 'control-label' = f.label :user_ids, "People", class: 'control-label'
.col-sm-10= users_select_tag(:user_ids, multiple: true, class: 'input-large') .col-sm-10= users_select_tag(:user_ids, multiple: true, class: 'input-large')
.form-group .form-group
= f.label :group_access, "Group Access", class: 'control-label' = f.label :access_level, "Group Access", class: 'control-label'
.col-sm-10= select_tag :group_access, options_for_select(UsersGroup.group_access_roles, @users_group.group_access), class: "project-access-select select2" .col-sm-10= select_tag :access_level, options_for_select(GroupMember.access_level_roles, @users_group.access_level), class: "project-access-select select2"
.form-actions .form-actions
= f.submit 'Add users into group', class: "btn btn-create" = f.submit 'Add users into group', class: "btn btn-create"
...@@ -21,11 +21,11 @@ ...@@ -21,11 +21,11 @@
= link_to leave_profile_group_path(@group), data: { confirm: leave_group_message(@group.name)}, method: :delete, class: "btn-tiny btn btn-remove", title: 'Remove user from group' do = link_to leave_profile_group_path(@group), data: { confirm: leave_group_message(@group.name)}, method: :delete, class: "btn-tiny btn btn-remove", title: 'Remove user from group' do
%i.icon-minus.icon-white %i.icon-minus.icon-white
- else - else
= link_to group_users_group_path(@group, member), data: { confirm: remove_user_from_group_message(@group, user) }, method: :delete, remote: true, class: "btn-tiny btn btn-remove", title: 'Remove user from group' do = link_to group_group_member_path(@group, member), data: { confirm: remove_user_from_group_message(@group, user) }, method: :delete, remote: true, class: "btn-tiny btn btn-remove", title: 'Remove user from group' do
%i.icon-minus.icon-white %i.icon-minus.icon-white
.edit-member.hide.js-toggle-content .edit-member.hide.js-toggle-content
= form_for [@group, member], remote: true do |f| = form_for [@group, member], remote: true do |f|
.alert.prepend-top-20 .alert.prepend-top-20
= f.select :group_access, options_for_select(UsersGroup.group_access_roles, member.group_access) = f.select :access_level, options_for_select(GroupMember.access_level_roles, member.access_level)
= f.submit 'Save', class: 'btn btn-save btn-small' = f.submit 'Save', class: 'btn btn-save btn-small'
...@@ -49,7 +49,7 @@ ...@@ -49,7 +49,7 @@
(#{@members.total_count}) (#{@members.total_count})
%ul.well-list %ul.well-list
- @members.each do |member| - @members.each do |member|
= render 'users_groups/users_group', member: member, show_roles: show_roles, show_controls: true = render 'groups/group_members/group_member', member: member, show_roles: show_roles, show_controls: true
= paginate @members, theme: 'gitlab' = paginate @members, theme: 'gitlab'
:coffeescript :coffeescript
......
...@@ -3,9 +3,9 @@ ...@@ -3,9 +3,9 @@
.modal-content .modal-content
.modal-header .modal-header
%a.close{href: "#", "data-dismiss" => "modal"} × %a.close{href: "#", "data-dismiss" => "modal"} ×
%h4 %h4
Keyboard Shortcuts Keyboard Shortcuts
%small %small
= link_to '(Show all)', '#', class: 'js-more-help-button' = link_to '(Show all)', '#', class: 'js-more-help-button'
.modal-body.shortcuts-cheatsheet .modal-body.shortcuts-cheatsheet
.col-lg-4 .col-lg-4
...@@ -15,11 +15,11 @@ ...@@ -15,11 +15,11 @@
%th %th
%th Global Shortcuts %th Global Shortcuts
%tr %tr
%td.shortcut %td.shortcut
.key s .key s
%td Focus Search %td Focus Search
%tr %tr
%td.shortcut %td.shortcut
.key ? .key ?
%td Show this dialog %td Show this dialog
%tbody %tbody
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
%i.icon-arrow-down %i.icon-arrow-down
%td Move selection down %td Move selection down
%tr %tr
%td.shortcut %td.shortcut
.key enter .key enter
%td Open Selection %td Open Selection
...@@ -48,25 +48,25 @@ ...@@ -48,25 +48,25 @@
%th %th
%th Global Dashboard %th Global Dashboard
%tr %tr
%td.shortcut %td.shortcut
.key g .key g
.key a .key a
%td %td
Go to the activity feed Go to the activity feed
%tr %tr
%td.shortcut %td.shortcut
.key g .key g
.key p .key p
%td %td
Go to projects Go to projects
%tr %tr
%td.shortcut %td.shortcut
.key g .key g
.key i .key i
%td %td
Go to issues Go to issues
%tr %tr
%td.shortcut %td.shortcut
.key g .key g
.key m .key m
%td %td
...@@ -76,43 +76,43 @@ ...@@ -76,43 +76,43 @@
%th %th
%th Project %th Project
%tr %tr
%td.shortcut %td.shortcut
.key g .key g
.key p .key p
%td %td
Go to the project's activity feed Go to the project's activity feed
%tr %tr
%td.shortcut %td.shortcut
.key g .key g
.key f .key f
%td %td
Go to files Go to files
%tr %tr
%td.shortcut %td.shortcut
.key g .key g
.key c .key c
%td %td
Go to commits Go to commits
%tr %tr
%td.shortcut %td.shortcut
.key g .key g
.key n .key n
%td %td
Go to network graph Go to network graph
%tr %tr
%td.shortcut %td.shortcut
.key g .key g
.key g .key g
%td %td
Go to graphs Go to graphs
%tr %tr
%td.shortcut %td.shortcut
.key g .key g
.key i .key i
%td %td
Go to issues Go to issues
%tr %tr
%td.shortcut %td.shortcut
.key g .key g
.key m .key m
%td %td
...@@ -180,11 +180,11 @@ ...@@ -180,11 +180,11 @@
%th %th
%th Issues %th Issues
%tr %tr
%td.shortcut %td.shortcut
.key a .key a
%td Change assignee %td Change assignee
%tr %tr
%td.shortcut %td.shortcut
.key m .key m
%td Change milestone %td Change milestone
%tbody{ class: 'hidden-shortcut merge_reuests', style: 'display:none' } %tbody{ class: 'hidden-shortcut merge_reuests', style: 'display:none' }
...@@ -192,11 +192,11 @@ ...@@ -192,11 +192,11 @@
%th %th
%th Merge Requests %th Merge Requests
%tr %tr
%td.shortcut %td.shortcut
.key a .key a
%td Change assignee %td Change assignee
%tr %tr
%td.shortcut %td.shortcut
.key m .key m
%td Change milestone %td Change milestone
......
...@@ -18,8 +18,7 @@ ...@@ -18,8 +18,7 @@
= javascript_include_tag "application" = javascript_include_tag "application"
= csrf_meta_tags = csrf_meta_tags
= include_gon = include_gon
:erb %meta{name: 'viewport', content: 'width=device-width, initial-scale=1.0'}
<meta name="viewport" content="width=device-width, initial-scale=1.0">
= render 'layouts/google_analytics' if extra_config.has_key?('google_analytics_id') = render 'layouts/google_analytics' if extra_config.has_key?('google_analytics_id')
= render 'layouts/piwik' if extra_config.has_key?('piwik_url') && extra_config.has_key?('piwik_site_id') = render 'layouts/piwik' if extra_config.has_key?('piwik_url') && extra_config.has_key?('piwik_site_id')
......
...@@ -4,10 +4,10 @@ ...@@ -4,10 +4,10 @@
%body{class: "#{app_theme} admin", :'data-page' => body_data_page} %body{class: "#{app_theme} admin", :'data-page' => body_data_page}
= render "layouts/broadcast" = render "layouts/broadcast"
= render "layouts/head_panel", title: "Admin area" = render "layouts/head_panel", title: "Admin area"
= render "layouts/flash"
%nav.main-nav.navbar-collapse.collapse %nav.main-nav.navbar-collapse.collapse
.container= render 'layouts/nav/admin' .container= render 'layouts/nav/admin'
.container .container
.content= yield .content
= render "layouts/flash"
= yield
= yield :embedded_scripts = yield :embedded_scripts
...@@ -4,9 +4,9 @@ ...@@ -4,9 +4,9 @@
%body{class: "#{app_theme} application", :'data-page' => body_data_page } %body{class: "#{app_theme} application", :'data-page' => body_data_page }
= render "layouts/broadcast" = render "layouts/broadcast"
= render "layouts/head_panel", title: "Dashboard" = render "layouts/head_panel", title: "Dashboard"
= render "layouts/flash"
%nav.main-nav.navbar-collapse.collapse %nav.main-nav.navbar-collapse.collapse
.container= render 'layouts/nav/dashboard' .container= render 'layouts/nav/dashboard'
.container .container
.content= yield .content
= render "layouts/flash"
= yield
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
%html{ lang: "en"} %html{ lang: "en"}
= render "layouts/head" = render "layouts/head"
%body.ui_basic.login-page %body.ui_basic.login-page
= render "layouts/flash"
.container .container
.content .content
.login-title .login-title
...@@ -10,6 +9,7 @@ ...@@ -10,6 +9,7 @@
%hr %hr
.container .container
.content .content
= render "layouts/flash"
.row .row
.col-md-7.brand-holder .col-md-7.brand-holder
- if brand_item - if brand_item
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
= render "layouts/head", title: "Error" = render "layouts/head", title: "Error"
%body{class: "#{app_theme} application"} %body{class: "#{app_theme} application"}
= render "layouts/head_panel", title: "" if current_user = render "layouts/head_panel", title: "" if current_user
= render "layouts/flash"
.container.navless-container .container.navless-container
= render "layouts/flash"
.error-page .error-page
= yield = yield
...@@ -4,9 +4,9 @@ ...@@ -4,9 +4,9 @@
%body{class: "#{app_theme} application", :'data-page' => body_data_page} %body{class: "#{app_theme} application", :'data-page' => body_data_page}
= render "layouts/broadcast" = render "layouts/broadcast"
= render "layouts/head_panel", title: "group: #{@group.name}" = render "layouts/head_panel", title: "group: #{@group.name}"
= render "layouts/flash"
%nav.main-nav.navbar-collapse.collapse %nav.main-nav.navbar-collapse.collapse
.container= render 'layouts/nav/group' .container= render 'layouts/nav/group'
.container .container
.content= yield .content
= render "layouts/flash"
= yield
...@@ -4,8 +4,7 @@ ...@@ -4,8 +4,7 @@
%body{class: "#{app_theme} application", :'data-page' => body_data_page} %body{class: "#{app_theme} application", :'data-page' => body_data_page}
= render "layouts/broadcast" = render "layouts/broadcast"
= render "layouts/head_panel", title: @title = render "layouts/head_panel", title: @title
= render "layouts/flash"
.container.navless-container .container.navless-container
.content .content
= render "layouts/flash"
= yield = yield
...@@ -4,9 +4,9 @@ ...@@ -4,9 +4,9 @@
%body{class: "#{app_theme} profile", :'data-page' => body_data_page} %body{class: "#{app_theme} profile", :'data-page' => body_data_page}
= render "layouts/broadcast" = render "layouts/broadcast"
= render "layouts/head_panel", title: "Profile" = render "layouts/head_panel", title: "Profile"
= render "layouts/flash"
%nav.main-nav.navbar-collapse.collapse %nav.main-nav.navbar-collapse.collapse
.container= render 'layouts/nav/profile' .container= render 'layouts/nav/profile'
.container .container
.content= yield .content
= render "layouts/flash"
= yield
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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