diff --git a/.rspec b/.rspec index 5902dd3af1fa401f8b3a8fd2f20cdeb562ee50ad..7488cbe7792e3f584f6bcc8fc283dc8036f25b50 100644 --- a/.rspec +++ b/.rspec @@ -1 +1 @@ ---colour --drb +--color --drb diff --git a/CHANGELOG b/CHANGELOG index 0bd687625638d65d53d7adf12d3d12c6ed7d8d95..5c27e93ff64fc953d10f56fd490f43ad515b9a41 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,19 @@ +v 6.1.0 + - Description field added to Merge Request + - API: Sudo api calls (Izaak Alpert) + - API: Group membership api (Izaak Alpert) + - Improved commit diff + - Improved large commit handling (Boyan Tabakov) + - Rewrite: Init script now less prone to errors and keeps better track of the service. + - Link issues, merge requests, and commits when they reference each other with GFM + - Close issues automatically when pushing commits with a special message + - Project internal ids for issues, mr, milestones + - Improve user removal from admin area + - Invalidate events cache when project was moved + - Remove deprecated classes and rake tasks + - Add event filter for group and project show pages + - Add links to create branch/tag from project home page + v 6.0.0 - Feature: Replace teams with group membership We introduce group membership in 6.0 as a replacement for teams. @@ -54,7 +70,7 @@ v 5.4.0 - Notify mentioned users with email v 5.3.0 - - Refactored services + - Refactored services - Campfire service added - HipChat service added - Fixed bug with LDAP + git over http diff --git a/Gemfile b/Gemfile index d950f43b20522ca0f6948479424c85e7afeba0cf..b201969bc36cba9339b3ac464a7390e8b72562f6 100644 --- a/Gemfile +++ b/Gemfile @@ -23,7 +23,7 @@ gem 'omniauth-github' # Extracting information from a git repository # Provide access to Gitlab::Git library -gem "gitlab_git", '2.1.0' +gem "gitlab_git", '2.1.1' # Ruby/Rack Git Smart-HTTP Server Handler gem 'gitlab-grack', '~> 1.0.1', require: 'grack' @@ -134,7 +134,7 @@ group :assets do gem "jquery-rails", "2.1.3" gem "jquery-ui-rails", "2.0.2" gem "modernizr", "2.6.2" - gem "raphael-rails", git: "https://github.com/gitlabhq/raphael-rails.git" + gem "raphael-rails", "~> 2.1.2" gem 'bootstrap-sass' gem "font-awesome-rails" gem "gemoji", "~> 1.2.1", require: 'emoji/railtie' @@ -187,7 +187,7 @@ group :development, :test do gem 'rb-inotify', require: linux_only('rb-inotify') # PhantomJS driver for Capybara - gem 'poltergeist', '~> 1.3.0' + gem 'poltergeist', '~> 1.4.1' gem 'spork', '~> 1.0rc' gem 'jasmine' diff --git a/Gemfile.lock b/Gemfile.lock index abec9243bcedfc36842ab7ade23ad2103f43face..ddca83d38c20b6ea679b312a0a68a22aac9b8b3e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -6,12 +6,6 @@ GIT activerecord (>= 2.3.0) rake (>= 0.8.7) -GIT - remote: https://github.com/gitlabhq/raphael-rails.git - revision: cb2c92a040b9b941a5f1aa1ea866cc26e944fe58 - specs: - raphael-rails (2.1.0) - GEM remote: https://rubygems.org/ specs: @@ -78,6 +72,7 @@ GEM railties (>= 3.0) sass-rails (>= 3.2) chunky_png (1.2.8) + cliver (0.2.1) code_analyzer (0.3.2) sexp_processor coderay (1.0.9) @@ -108,7 +103,7 @@ GEM d3_rails (3.1.10) railties (>= 3.1.0) daemons (1.1.9) - database_cleaner (1.0.1) + database_cleaner (1.1.1) debug_inspector (0.0.2) descendants_tracker (0.0.1) devise (2.2.5) @@ -138,8 +133,6 @@ GEM multipart-post (~> 1.1) faraday_middleware (0.9.0) faraday (>= 0.7.4, < 0.9) - faye-websocket (0.4.7) - eventmachine (>= 0.12.0) ffaker (1.16.1) ffi (1.9.0) fog (1.3.1) @@ -186,7 +179,7 @@ GEM gitlab-pygments.rb (0.3.2) posix-spawn (~> 0.3.6) yajl-ruby (~> 1.1.0) - gitlab_git (2.1.0) + gitlab_git (2.1.1) activesupport (~> 3.2.13) github-linguist (~> 2.3.4) gitlab-grit (~> 2.6.0) @@ -281,7 +274,7 @@ GEM mime-types (~> 1.16) treetop (~> 1.4.8) method_source (0.8.1) - mime-types (1.23) + mime-types (1.25) minitest (4.7.4) modernizr (2.6.2) sprockets (~> 2.0) @@ -321,10 +314,11 @@ GEM omniauth-oauth (~> 1.0) orm_adapter (0.4.0) pg (0.15.1) - poltergeist (1.3.0) + poltergeist (1.4.1) capybara (~> 2.1.0) - faye-websocket (>= 0.4.4, < 0.5.0) - http_parser.rb (~> 0.5.3) + cliver (~> 0.2.1) + multi_json (~> 1.0) + websocket-driver (>= 0.2.0) polyglot (0.3.3) posix-spawn (0.3.6) pry (0.9.12.2) @@ -380,6 +374,7 @@ GEM thor (>= 0.14.6, < 2.0) raindrops (0.11.0) rake (10.1.0) + raphael-rails (2.1.2) rb-fsevent (0.9.3) rb-inotify (0.9.0) ffi (>= 0.5.0) @@ -389,24 +384,24 @@ GEM json (~> 1.4) redcarpet (2.2.2) redis (3.0.4) - redis-actionpack (3.2.3) - actionpack (~> 3.2.3) - redis-rack (~> 1.4.0) + redis-actionpack (3.2.4) + actionpack (~> 3.2.0) + redis-rack (~> 1.4.4) + redis-store (~> 1.1.4) + redis-activesupport (3.2.4) + activesupport (~> 3.2.0) redis-store (~> 1.1.0) - redis-activesupport (3.2.3) - activesupport (~> 3.2.3) - redis-store (~> 1.1.0) - redis-namespace (1.3.0) + redis-namespace (1.3.1) redis (~> 3.0.0) - redis-rack (1.4.2) - rack (~> 1.4.1) - redis-store (~> 1.1.0) - redis-rails (3.2.3) - redis-actionpack (~> 3.2.3) - redis-activesupport (~> 3.2.3) - redis-store (~> 1.1.0) - redis-store (1.1.3) - redis (>= 2.2.0) + redis-rack (1.4.4) + rack (~> 1.4.0) + redis-store (~> 1.1.4) + redis-rails (3.2.4) + redis-actionpack (~> 3.2.4) + redis-activesupport (~> 3.2.4) + redis-store (~> 1.1.4) + redis-store (1.1.4) + redis (>= 2.2) ref (1.0.5) rest-client (1.6.7) mime-types (>= 1.16) @@ -455,11 +450,11 @@ GEM sexp_processor (4.2.1) shoulda-matchers (2.1.0) activesupport (>= 3.0.0) - sidekiq (2.12.4) + sidekiq (2.14.0) celluloid (>= 0.14.1) connection_pool (>= 1.0.0) json - redis (>= 3.0) + redis (>= 3.0.4) redis-namespace simple_oauth (0.1.9) simplecov (0.7.1) @@ -539,6 +534,7 @@ GEM addressable (>= 2.2.7) crack (>= 0.3.2) websocket (1.0.7) + websocket-driver (0.3.0) xpath (2.0.0) nokogiri (~> 1.3) yajl-ruby (1.1.0) @@ -576,7 +572,7 @@ DEPENDENCIES gitlab-gollum-lib (~> 1.0.1) gitlab-grack (~> 1.0.1) gitlab-pygments.rb (~> 0.3.2) - gitlab_git (= 2.1.0) + gitlab_git (= 2.1.1) gitlab_meta (= 6.0) gitlab_omniauth-ldap (= 1.0.3) gon @@ -604,14 +600,14 @@ DEPENDENCIES omniauth-google-oauth2 omniauth-twitter pg - poltergeist (~> 1.3.0) + poltergeist (~> 1.4.1) pry quiet_assets (~> 1.0.1) rack-mini-profiler rails (= 3.2.13) rails-dev-tweaks rails_best_practices - raphael-rails! + raphael-rails (~> 2.1.2) rb-fsevent rb-inotify redcarpet (~> 2.2.2) diff --git a/PROCESS.md b/PROCESS.md index 17684ae5c7134b44dd054bf5bea3e7378897af76..668cacc870a3e6110d5d49c15f5aae9f2c94b5a5 100644 --- a/PROCESS.md +++ b/PROCESS.md @@ -2,23 +2,47 @@ ## Purpose of describing the contributing process -Below we describe the contributing process for two reasons. Contributors know what to expect from maintainers (initial, response within xx days, friendly treatment, etc). And maintainers know what to expect from contributors (use latest version, confirm the issue is addressed, friendly treatment, etc). +Below we describe the contributing process to GitLab for two reasons. So that contributors know what to expect from maintainers (possible responses, friendly treatment, etc.). And so that maintainers know what to expect from contributors (use the latest version, ensure that the issue is addressed, friendly treatment, etc.). -## How we handle issues +## Common actions + +### Issue team +- Looks for issues without workflow labels and triages issue +- Monitors pull requests +- Closes invalid issues and pull requests with a comment (duplicates, [feature requests](#feature-requests), [fixed in newer version](#issue-fixed-in-newer-version), [issue report for old version](#issue-report-for-old-version), not a problem in GitLab, etc.) +- Assigns appropriate [labels](#how-we-handle-issues) +- Asks for feedback from issue reporter/pull request initiator ([invalid issue reports](#improperly-formatted-issue), [format code](#code-format), etc.) +- Asks for feedback from the relevant developer(s) based on the [list of members and their specialities](http://gitlab.org/team/) +- Monitors all issues/pull requests for feedback (but especially ones commented on since automatically watching them): +- Closes issues with no feedback from the reporter for two weeks +- Closes stale pull requests -The priority should be mentioning people that can help and assigning workflow labels. Workflow labels are purposely not very detailed since that would be hard to keep updated as you would need to reevaluate them after every comment. We optionally use functional labels on demand when want to group related issues to get an overview (for example all issues related to RVM, to tackle them in one go) and to add details to the issue. +### Development team + +- Responds to issues and pull requests the issue team mentions them in +- Monitors for new issues in _Awaiting developer action/feedback_ with no developer activity (once a week) +- Monitors for new pull requests (at least once a week) +- Manages their work queue by looking at issues and pull requests assigned to them +- Close fixed issues (via commit messages or manually) +- Codes [new features](http://feedback.gitlab.com/forums/176466-general/filters/top)! +- Response guidelines +- Be kind to people trying to contribute. Be aware that people can be a non-native or a native English speaker, they might not understand thing or they might be very sensitive to how your word things. Use emoji to express your feelings (heart, star, smile, etc.). Some good tips about giving feedback to pull requests is in the [Thoughtbot code review guide](https://github.com/thoughtbot/guides/tree/master/code-review). -If an issue is complex and needs the attention of a specific person, assignment is a good option but assigning issues might discourage other people from contributing to that issue. We need all the contributions we can get so this should never be discouraged. Also, an assigned person might not have time for a few weeks, so others should feel free to takeover. +## Priorities of the issue team -Priority (from high to low): +1. Mentioning people (critical) +2. Workflow labels (normal) +3. Functional labels (minor) +4. Assigning issues (avoid if possible) -1. Mentioning people (very important) -2. Workflow labels -3. Functional labels (less important) -4. Assigning issues (optional) +## Mentioning people + +The most important thing is making sure valid issues receive feedback from the development team. Therefore the priority is mentioning developers that can help on those issue. Please select someone with relevant experience from [GitLab core team](http://gitlab.org/team/). If there is nobody mentioned with that expertise look in the commit history for the affected files to find someone. Avoid mentioning the lead developer, this is the person that is least likely to give a timely response. If the involvement of the lead developer is needed the other core team members will mention this person. ## Workflow labels +Workflow labels are purposely not very detailed since that would be hard to keep updated as you would need to reevaluate them after every comment. We optionally use functional labels on demand when want to group related issues to get an overview (for example all issues related to RVM, to tackle them in one go) and to add details to the issue. + - _Awaiting feedback_: Feedback pending from the reporter - _Awaiting confirmation of fix_: The issue should already be solved in **master** (generally you can avoid this workflow item and just close the issue right away) - _Attached PR_: There is a PR attached and the discussion should happen there @@ -29,6 +53,10 @@ Priority (from high to low): These labels describe what development specialities are involved such as: PostgreSQL, UX, LDAP. +## Assigning issues + +If an issue is complex and needs the attention of a specific person, assignment is a good option but assigning issues might discourage other people from contributing to that issue. We need all the contributions we can get so this should never be discouraged. Also, an assigned person might not have time for a few weeks, so others should feel free to takeover. + ## Label colors - Light orange `#fef2c0`: workflow labels for issue team members (awaiting feedback, awaiting confirmation of fix) - Bright orange `#eb6420`: workflow labels for core team members (attached PR, awaiting developer action/feedback) @@ -37,32 +65,6 @@ These labels describe what development specialities are involved such as: Postgr - Feature request (see copy & paste response: [Feature requests](#feature-requests)) - Support (see copy & paste response: [Support requests and configuration questions](#support-requests-and-configuration-questions) -## Common actions - -### Issue team -- Looks for issues without workflow labels and triages issue -- Monitors pull requests -- Closes invalid issues and pull requests with a comment (duplicates, [feature requests](#feature-requests), [fixed in newer version](#issue-fixed-in-newer-version), [issue report for old version](#issue-report-for-old-version), not a problem in GitLab, etc.) -- Assigns appropriate [labels](#how-we-handle-issues) -- Asks for feedback from issue reporter/pull request initiator ([invalid issue reports](#improperly-formatted-issue), [format code](#code-format), etc.) -- Asks for feedback from the relevant developer(s) based on the [list of members and their specialities](http://gitlab.org/team/) -- Monitors all issues/pull requests for feedback (but especially ones commented on since automatically watching them): -- Assigns issues to developers if they indicate they are fixing it -- Assigns pull requests to developers if they indicate they will take care of merge -- Closes issues with no feedback from the reporter for two weeks -- Closes stale pull requests - -### Development team - -- Responds to issues and pull requests the issue team mentions them in -- Monitors for new issues in _Awaiting developer action/feedback_ with no developer activity (once a week) -- Monitors for new pull requests (at least once a week) -- Manages their work queue by looking at issues and pull requests assigned to them -- Close fixed issues (via commit messages or manually) -- Codes [new features](http://feedback.gitlab.com/forums/176466-general/filters/top)! -- Response guidelines -- Be kind to people trying to contribute. Be aware that people can be a non-native or a native English speaker, they might not understand thing or they might be very sensitive to how your word things. Use emoji to express your feelings (hearth, star, smile, etc.). Some good tips about giving feedback to pull requests is in the [Thoughtbot code review guide](https://github.com/thoughtbot/guides/tree/master/code-review). - ## Copy & paste responses ### Improperly formatted issue diff --git a/README.md b/README.md index d4c7c2740e4b8d64c6bcf39eb029076b1d89f6d1..a4c5c0d0534223d120ccb7332d464ea276ba1ecb 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ * powered by Ruby on Rails * completely free and open source (MIT license) -* used by more than 10.000 organizations to keep their code secure +* used by more than 25.000 organizations to keep their code secure ### Code status @@ -80,7 +80,7 @@ Since 2011 GitLab is released on the 22nd of every month. Every new release incl * [Changelog](CHANGELOG) -* Features that will be in the next releases are listed on [the feedback and suggestions forum](http://feedback.gitlab.com/forums/176466-general) with the status ["started"](http://feedback.gitlab.com/forums/176466-general/status/796456) and [completed](http://feedback.gitlab.com/forums/176466-general/status/796457). +* Features that will be in the next releases are listed on [the feedback and suggestions forum](http://feedback.gitlab.com/forums/176466-general) with the status [started](http://feedback.gitlab.com/forums/176466-general/status/796456) and [completed](http://feedback.gitlab.com/forums/176466-general/status/796457). ### Run in production mode diff --git a/VERSION b/VERSION index 09b254e90c61ed28bb68a54752cf04f6a736a7d3..f5999028a39018da71d3d8db2178ce054e5b77ad 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -6.0.0 +6.1.0.beta1 diff --git a/app/assets/javascripts/activities.js.coffee b/app/assets/javascripts/activities.js.coffee new file mode 100644 index 0000000000000000000000000000000000000000..fdefbfb92bd9f148c3cd0611672357a79994e370 --- /dev/null +++ b/app/assets/javascripts/activities.js.coffee @@ -0,0 +1,31 @@ +class Activities + constructor: -> + Pager.init 20, true + $(".event_filter_link").bind "click", (event) => + event.preventDefault() + @toggleFilter($(event.currentTarget)) + @reloadActivities() + + reloadActivities: -> + $(".content_list").html '' + Pager.init 20, true + + + toggleFilter: (sender) -> + sender.parent().toggleClass "inactive" + event_filters = $.cookie("event_filter") + filter = sender.attr("id").split("_")[0] + if event_filters + event_filters = event_filters.split(",") + else + event_filters = new Array() + + index = event_filters.indexOf(filter) + if index is -1 + event_filters.push filter + else + event_filters.splice index, 1 + + $.cookie "event_filter", event_filters.join(","), { path: '/' } + +@Activities = Activities diff --git a/app/assets/javascripts/admin.js.coffee b/app/assets/javascripts/admin.js.coffee index f75008e2b603dc61cbd11e10e6229f12c8331d04..6230fe7f93f53351fdaecdf2e90f7c9051146831 100644 --- a/app/assets/javascripts/admin.js.coffee +++ b/app/assets/javascripts/admin.js.coffee @@ -23,10 +23,16 @@ class Admin e.preventDefault() $(this).hide() modal.show() - + $('.change-owner-cancel-link').bind "click", (e) -> e.preventDefault() modal.hide() $('.change-owner-link').show() + $('li.users_project').bind 'ajax:success', -> + Turbolinks.visit(location.href) + + $('li.users_group').bind 'ajax:success', -> + Turbolinks.visit(location.href) + @Admin = Admin diff --git a/app/assets/javascripts/dashboard.js.coffee b/app/assets/javascripts/dashboard.js.coffee index 4871936d5b2a43de3f1a6962c60fad5f15af5135..d2bd9e7362b5c87aed0b99af04763cb5f1dcf911 100644 --- a/app/assets/javascripts/dashboard.js.coffee +++ b/app/assets/javascripts/dashboard.js.coffee @@ -1,13 +1,7 @@ class Dashboard constructor: -> - Pager.init 20, true @initSidebarTab() - $(".event_filter_link").bind "click", (event) => - event.preventDefault() - @toggleFilter($(event.currentTarget)) - @reloadActivities() - $(".dash-filter").keyup -> terms = $(this).val() uiBox = $(this).parents('.ui-box').first() @@ -24,27 +18,6 @@ class Dashboard - reloadActivities: -> - $(".content_list").html '' - Pager.init 20, true - - toggleFilter: (sender) -> - sender.parent().toggleClass "inactive" - event_filters = $.cookie("event_filter") - filter = sender.attr("id").split("_")[0] - if event_filters - event_filters = event_filters.split(",") - else - event_filters = new Array() - - index = event_filters.indexOf(filter) - if index is -1 - event_filters.push filter - else - event_filters.splice index, 1 - - $.cookie "event_filter", event_filters.join(","), { path: '/' } - initSidebarTab: -> key = "dashboard_sidebar_filter" diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee index b0b947dd476bdc4d3d89961fcc44ccd44cb44c3f..e264e281309eea1c2b01f38ef26eb0274dc968a3 100644 --- a/app/assets/javascripts/dispatcher.js.coffee +++ b/app/assets/javascripts/dispatcher.js.coffee @@ -18,12 +18,15 @@ class Dispatcher switch page when 'projects:issues:index' Issues.init() + when 'projects:issues:new', 'projects:merge_requests:new' + GitLab.GfmAutoComplete.setup() when 'dashboard:show' new Dashboard() + new Activities() when 'projects:commit:show' new Commit() when 'groups:show', 'projects:show' - Pager.init(20, true) + new Activities() when 'projects:new', 'projects:edit' new Project() when 'projects:walls:show' diff --git a/app/assets/javascripts/flash.js.coffee b/app/assets/javascripts/flash.js.coffee new file mode 100644 index 0000000000000000000000000000000000000000..f8b7789884fd3d6d57e63e2da3ec611a68f5d2b7 --- /dev/null +++ b/app/assets/javascripts/flash.js.coffee @@ -0,0 +1,15 @@ +class Flash + constructor: (message, type)-> + flash = $(".flash-container") + flash.html("") + + $('
', + class: "flash-#{type}", + text: message + ).appendTo(".flash-container") + + flash.click -> $(@).fadeOut() + flash.show() + setTimeout (-> flash.fadeOut()), 5000 + +@Flash = Flash diff --git a/app/assets/javascripts/gfm_auto_complete.js.coffee b/app/assets/javascripts/gfm_auto_complete.js.coffee index e22761e57a4af25132635c506120ba7fc4c33e18..77091da8f61421c6075e75217e7e97ae81f42ccc 100644 --- a/app/assets/javascripts/gfm_auto_complete.js.coffee +++ b/app/assets/javascripts/gfm_auto_complete.js.coffee @@ -44,7 +44,7 @@ GitLab.GfmAutoComplete = tpl: @Issues.template callbacks: before_save: (issues) -> - $.map issues, (i) -> id: i.id, title: sanitize(i.title), search: "#{i.id} #{i.title}" + $.map issues, (i) -> id: i.iid, title: sanitize(i.title), search: "#{i.iid} #{i.title}" input.one "focus", => $.getJSON(@dataSource).done (data) -> diff --git a/app/assets/javascripts/merge_requests.js.coffee b/app/assets/javascripts/merge_requests.js.coffee index 153198ca5c5cfd8f701aa5b39074e277cb1d5aec..5400bc5c1ad08199d92e0007f2ec785307593bbe 100644 --- a/app/assets/javascripts/merge_requests.js.coffee +++ b/app/assets/javascripts/merge_requests.js.coffee @@ -11,7 +11,7 @@ class MergeRequest constructor: (@opts) -> this.$el = $('.merge-request') - @diffs_loaded = false + @diffs_loaded = if @opts.action == 'diffs' then true else false @commits_loaded = false this.activateTab(@opts.action) diff --git a/app/assets/javascripts/profile.js.coffee b/app/assets/javascripts/profile.js.coffee index 213133bc96523372763a6b09e8005c55dbda3ed7..e7974611cbec6461c93335d56ad6848d5fbd54e8 100644 --- a/app/assets/javascripts/profile.js.coffee +++ b/app/assets/javascripts/profile.js.coffee @@ -1,13 +1,9 @@ $ -> $('.edit_user .application-theme input, .edit_user .code-preview-theme input').click -> - # Hide any previous submission feedback - $('.edit_user .update-feedback').hide() - # Submit the form $('.edit_user').submit() - # Go up the hierarchy and show the corresponding submission feedback element - $(@).closest('fieldset').find('.update-feedback').show('highlight', {color: '#DFF0D8'}, 500) + new Flash("Appearance settings saved", "notice") $('.update-username form').on 'ajax:before', -> $('.loading-gif').show() @@ -17,6 +13,6 @@ $ -> $('.update-username form').on 'ajax:complete', -> $(this).find('.btn-save').enableButton() $(this).find('.loading-gif').hide() - + $('.update-notifications').on 'ajax:complete', -> $(this).find('.btn-save').enableButton() diff --git a/app/assets/javascripts/project.js.coffee b/app/assets/javascripts/project.js.coffee index 780292daa11b6bbc0814df40548393efd0f293e4..83236b348147a59cedccbc720d517f41c6749041 100644 --- a/app/assets/javascripts/project.js.coffee +++ b/app/assets/javascripts/project.js.coffee @@ -35,6 +35,7 @@ $ -> $('a, button', scope).removeClass 'active' $(@).addClass 'active' $('#project_clone', scope).val $(@).data 'clone' + $(".clone").text("").append 'git remote add origin ' + $(@).data 'clone' # Ref switcher $('.project-refs-select').on 'change', -> diff --git a/app/assets/stylesheets/common.scss b/app/assets/stylesheets/common.scss index d9669b5e79d907fa2afbca5cf6a592e9d2408d74..6d80b22b3aa6fd817d27bb2786e7bfd2c1098f86 100644 --- a/app/assets/stylesheets/common.scss +++ b/app/assets/stylesheets/common.scss @@ -55,6 +55,7 @@ table a code { bottom: 0; width: 100%; opacity: 0.8; + z-index: 100; .flash-notice { background: #49C; @@ -372,3 +373,12 @@ table { text-align: center; } } + +.dashboard-intro-icon { + float: left; + font-size: 32px; + color: #AAA; + padding: 5px 0; + width: 50px; + min-height: 100px; +} diff --git a/app/assets/stylesheets/gitlab_bootstrap/common.scss b/app/assets/stylesheets/gitlab_bootstrap/common.scss index 372cbe50fd0e7bea1619fff1523f84d4861703db..bc6c786da50c7667be2288e59e5e0b51e7b7eac9 100644 --- a/app/assets/stylesheets/gitlab_bootstrap/common.scss +++ b/app/assets/stylesheets/gitlab_bootstrap/common.scss @@ -28,7 +28,7 @@ .hint { font-style: italic; color: #999; } .light { color: #888 } .tiny { font-weight: normal } -.vtop { vertical-align: top; } +.vtop { vertical-align: top !important; } /** ALERT MESSAGES **/ @@ -56,23 +56,6 @@ line-height: 24px; } -/** FORMS **/ -input[type='search'].search-text-input { - background-image: url("icon-search.png"); - background-repeat: no-repeat; - background-position: 10px; - padding-left: 25px; - @include border-radius(4px); - border: 1px solid #ccc; -} - -input[type='text'].danger { - background: #F2DEDE!important; - border-color: #D66; - text-shadow: 0 1px 1px #fff -} - -fieldset legend { font-size: 15px; } .tab-content { overflow: visible; diff --git a/app/assets/stylesheets/gitlab_bootstrap/forms.scss b/app/assets/stylesheets/gitlab_bootstrap/forms.scss index d1feb55440409bec88fe38747c6d240cc2cf443f..a2612166c7858f6feedb29af472bb794ead65edc 100644 --- a/app/assets/stylesheets/gitlab_bootstrap/forms.scss +++ b/app/assets/stylesheets/gitlab_bootstrap/forms.scss @@ -6,10 +6,9 @@ form { } } -input { - &.input-xpadding { - padding: 6px 10px; - } +input.input-xpadding, +.add-on.input-xpadding { + padding: 6px 10px; } .control-group { @@ -31,3 +30,22 @@ input { } } +input[type='search'].search-text-input { + background-image: url("icon-search.png"); + background-repeat: no-repeat; + background-position: 10px; + padding-left: 25px; + @include border-radius(4px); + border: 1px solid #ccc; +} + +input[type='text'].danger { + background: #F2DEDE!important; + border-color: #D66; + text-shadow: 0 1px 1px #fff +} + +fieldset legend { + font-size: 16px; + margin-bottom: 10px; +} diff --git a/app/assets/stylesheets/gitlab_bootstrap/mixins.scss b/app/assets/stylesheets/gitlab_bootstrap/mixins.scss index 8676cc624ce313fa36e4e1bb51fc528c435b9bbf..e7daf8f43b42d47a196fab20e347ee14a6ce5f9a 100644 --- a/app/assets/stylesheets/gitlab_bootstrap/mixins.scss +++ b/app/assets/stylesheets/gitlab_bootstrap/mixins.scss @@ -89,6 +89,12 @@ h2 { margin-top: 25px;} h3 { margin-top: 20px;} h4 { margin-top: 15px;} + + blockquote p { + color: #888; + font-size: 14px; + line-height: 1.5; + } } @mixin page-title { diff --git a/app/assets/stylesheets/gitlab_bootstrap/nav.scss b/app/assets/stylesheets/gitlab_bootstrap/nav.scss index 40616f7540a3514a152b6b3958bfcac73cc4e58f..aa4cb1ed5fd12af75b634eb39165527170fe9f66 100644 --- a/app/assets/stylesheets/gitlab_bootstrap/nav.scss +++ b/app/assets/stylesheets/gitlab_bootstrap/nav.scss @@ -22,6 +22,7 @@ background: #F1F1F1; color: $style_color; font-weight: bold; + text-shadow: 0 1px 1px #fff; } &.nav-stacked-menu { diff --git a/app/assets/stylesheets/sections/commits.scss b/app/assets/stylesheets/sections/commits.scss index faae0e127d971c23d8aad01d485b9b9ef0d750c4..b0cf8b16260f82c68b3082959e419df22430c125 100644 --- a/app/assets/stylesheets/sections/commits.scss +++ b/app/assets/stylesheets/sections/commits.scss @@ -345,37 +345,6 @@ } -/** - * COMMIT ROW - */ -.commit { - .browse_code_link_holder { - float: right; - } - - .committed_ago { - float: right; - @extend .cgray; - } - - .notes_count { - float: right; - margin-right: 10px; - } - - code { - background: #FCEEC1; - color: $style_color; - } - - .commit_short_id { - float: left; - @extend .lined; - min-width: 65px; - font-family: $monospace_font; - } -} - .file-stats a { color: $style_color; } @@ -463,24 +432,6 @@ line-height: 2; } -li.commit { - .avatar { - width: 24px; - top:-5px; - margin-right: 5px; - margin-left: 10px; - } - - code { - padding: 2px 2px 0; - margin-top: -2px; - &:hover { - color: black; - border: 1px solid #ccc; - } - } -} - .commit-breadcrumb { padding: 0; } @@ -496,3 +447,61 @@ li.commit { font-weight: bold; } } + +.lists-separator { + margin: 10px 0; + border-top: 1px dashed #CCC; +} + +/** + * COMMIT ROW + */ +li.commit { + padding: 8px; + + .commit-row-title { + font-size: 14px; + margin-bottom: 2px; + + .notes_count { + float: right; + margin-right: 10px; + } + + .commit_short_id { + min-width: 65px; + font-family: $monospace_font; + } + + .commit-row-message { + color: #555; + font-weight: bolder; + &:hover { + color: #444; + text-decoration: underline; + } + } + } + + .commit-row-info { + a { + color: #777; + } + + .committed_ago { + float: right; + @extend .cgray; + } + } + + &.inline-commit { + .commit-row-title { + font-size: 13px; + } + + .committed_ago { + float: right; + @extend .cgray; + } + } +} diff --git a/app/assets/stylesheets/sections/events.scss b/app/assets/stylesheets/sections/events.scss index d94d97c8d9aa893490572e04898ab1bf532c46e3..fd5c80f988cb81ffd9aa2bfe12ee16c947424036 100644 --- a/app/assets/stylesheets/sections/events.scss +++ b/app/assets/stylesheets/sections/events.scss @@ -56,10 +56,6 @@ margin-left: 35px; margin-right: 100px; - .commit p { - color: #666; - padding-top: 5px; - } .event-info { color: #666; } @@ -80,6 +76,10 @@ margin-left: 0px; max-width: 200px; } + + p:last-child { + margin-bottom: 0; + } } .event-note-icon { color: #777; @@ -103,13 +103,6 @@ } } - ul { - .avatar { - width: 18px; - margin: 2px 4px; - } - } - &:last-child { border:none } .event_commits { @@ -120,12 +113,14 @@ background: transparent; padding: 3px; border: none; - font-size: 12px; + color: #666; + .commit-row-title { + font-size: 12px; + } } &.commits-stat { display: block; padding: 3px; - margin-top: 3px; &:hover { background: none; diff --git a/app/assets/stylesheets/sections/merge_requests.scss b/app/assets/stylesheets/sections/merge_requests.scss index bdab306a57d172ad79e2522bb1867da456ef78bc..aa61cae4b9a02d6cca0eae422aafc1a4cd17e18c 100644 --- a/app/assets/stylesheets/sections/merge_requests.scss +++ b/app/assets/stylesheets/sections/merge_requests.scss @@ -64,13 +64,6 @@ margin: 0; padding: 0; padding: 5px 0; - .avatar { position:relative } - .commit-author-name, - .dash, - .committed_ago, - .browse_code_link_holder { - display: none; - } list-style: none; &:hover { background: none; @@ -117,7 +110,7 @@ .merge-request-angle { text-align: center; - margin-top: 45px; + margin: 0; } .merge-request-form-info { diff --git a/app/assets/stylesheets/sections/profile.scss b/app/assets/stylesheets/sections/profile.scss index 06a4046f91671bb841ea6cb56d2adac876f30d2c..5c7516ce6f910651a85153a9b6e3eade2c76858c 100644 --- a/app/assets/stylesheets/sections/profile.scss +++ b/app/assets/stylesheets/sections/profile.scss @@ -1,9 +1,3 @@ -.save-status-fixed { - position: fixed; - left: 20px; - bottom: 50px; -} - .update-notifications { margin-bottom: 0; label { diff --git a/app/assets/stylesheets/sections/themes.scss b/app/assets/stylesheets/sections/themes.scss index c5487f9f40cb79dee5c48982105f2e98ad24f41c..cd1aa2b011a3dd52aa010ad26ad30ff2234f58b1 100644 --- a/app/assets/stylesheets/sections/themes.scss +++ b/app/assets/stylesheets/sections/themes.scss @@ -1,10 +1,3 @@ -.application-theme, .code-preview-theme { - .update-feedback { - color: #468847; - float: right; - } -} - .themes_opts { padding-left: 20px; diff --git a/app/assets/stylesheets/sections/tree.scss b/app/assets/stylesheets/sections/tree.scss index b8b82bcf68c6bb2bf849512b03a52cceaa3915f5..850ed831dfb4e0d00a4cda492dedb42cd407873c 100644 --- a/app/assets/stylesheets/sections/tree.scss +++ b/app/assets/stylesheets/sections/tree.scss @@ -15,6 +15,12 @@ .tree-table { @include border-radius(0); .tree-item { + td { + padding: 8px 10px; + strong { + font-weight: normal; + } + } &:hover { td { background: $hover; diff --git a/app/assets/stylesheets/themes/ui_color.scss b/app/assets/stylesheets/themes/ui_color.scss index 07e854b73d8ec753f43b2405b646b443b2dec39a..1fd54ff18a664df6d57acdd1217926879d5f4460 100644 --- a/app/assets/stylesheets/themes/ui_color.scss +++ b/app/assets/stylesheets/themes/ui_color.scss @@ -17,6 +17,7 @@ &.navbar-gitlab { .navbar-inner { background: #547; + border-bottom: 1px solid #435; .app_logo { &:hover { background-color: #435; diff --git a/app/assets/stylesheets/themes/ui_gray.scss b/app/assets/stylesheets/themes/ui_gray.scss index 4f88c631e4d52abf35b79eb74a8632d44cb9e9b0..41c08c840e2c8da2b87b7cbc2fb0c0cbdc531f8f 100644 --- a/app/assets/stylesheets/themes/ui_gray.scss +++ b/app/assets/stylesheets/themes/ui_gray.scss @@ -17,6 +17,7 @@ &.navbar-gitlab { .navbar-inner { background: #373737; + border-bottom: 1px solid #272727; .app_logo { &:hover { background-color: #272727; diff --git a/app/assets/stylesheets/themes/ui_modern.scss b/app/assets/stylesheets/themes/ui_modern.scss index 68318f9301687d9ac4b87e1fa826f076cbb3c371..6173757082e08122cd1d7f50665b2889206d8838 100644 --- a/app/assets/stylesheets/themes/ui_modern.scss +++ b/app/assets/stylesheets/themes/ui_modern.scss @@ -17,6 +17,7 @@ &.navbar-gitlab { .navbar-inner { background: #345; + border-bottom: 1px solid #234; .app_logo { &:hover { background-color: #234; diff --git a/app/contexts/commit_load_context.rb b/app/contexts/commit_load_context.rb index 2cf5420d62dae3900dc935f6bf546559d696ae58..2930c5b1668f1124f676fe4a630b08b71183b592 100644 --- a/app/contexts/commit_load_context.rb +++ b/app/contexts/commit_load_context.rb @@ -20,7 +20,8 @@ class CommitLoadContext < BaseContext result[:notes_count] = project.notes.for_commit_id(commit.id).count begin - result[:suppress_diff] = true if commit.diffs.size > Commit::DIFF_SAFE_SIZE && !params[:force_show_diff] + result[:suppress_diff] = true if commit.diff_suppress? && !params[:force_show_diff] + result[:force_suppress_diff] = commit.diff_force_suppress? rescue Grit::Git::GitTimeout result[:suppress_diff] = true result[:status] = :huge_commit diff --git a/app/contexts/projects/create_context.rb b/app/contexts/projects/create_context.rb index 1ee3e45907464a0df5269ec485deefbe996bb8a0..9fd24e0eeeacb69185591d1a12a1af6306106c12 100644 --- a/app/contexts/projects/create_context.rb +++ b/app/contexts/projects/create_context.rb @@ -16,7 +16,8 @@ module Projects wiki_enabled: default_features.wiki, wall_enabled: default_features.wall, snippets_enabled: default_features.snippets, - merge_requests_enabled: default_features.merge_requests + merge_requests_enabled: default_features.merge_requests, + public: default_features.public } @project = Project.new(default_opts.merge(params)) diff --git a/app/controllers/admin/members_controller.rb b/app/controllers/admin/members_controller.rb deleted file mode 100644 index d920193050166e060ca30ed16b366baef63a2d85..0000000000000000000000000000000000000000 --- a/app/controllers/admin/members_controller.rb +++ /dev/null @@ -1,9 +0,0 @@ -class Admin::MembersController < Admin::ApplicationController - def destroy - user = User.find_by_username(params[:id]) - project = Project.find_with_namespace(params[:project_id]) - project.users_projects.where(user_id: user).first.destroy - - redirect_to :back - end -end diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 7703ae8b249500ca7e6b6995f3afbec45a6478d6..70bbe306562e9035700a490f01736dd93cb191df 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -13,7 +13,7 @@ class Admin::UsersController < Admin::ApplicationController end def new - @user = User.new.with_defaults + @user = User.build_user end def edit @@ -44,7 +44,7 @@ class Admin::UsersController < Admin::ApplicationController password_expires_at: Time.now } - @user = User.new(params[:user].merge(opts), as: :admin) + @user = User.build_user(params[:user].merge(opts), as: :admin) @user.admin = (admin && admin.to_i > 0) @user.created_by_id = current_user.id @@ -83,9 +83,10 @@ class Admin::UsersController < Admin::ApplicationController end def destroy - if user.personal_projects.count > 0 - redirect_to admin_users_path, alert: "User is a project owner and can't be removed." and return - end + # 1. Remove groups where user is the only owner + user.solo_owned_groups.map(&:destroy) + + # 2. Remove user with all authored content including personal projects user.destroy respond_to do |format| diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 9a43381f405cab76729f5e3435ffe5014af4ea8f..b93bf0f98eba9ce18f544f687eae05f7ce26136e 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -95,10 +95,6 @@ class ApplicationController < ActionController::Base return access_denied! unless can?(current_user, :push_code, project) end - def authorize_create_team! - return access_denied! unless can?(current_user, :create_team, nil) - end - def access_denied! render "errors/access_denied", layout: "errors", status: 404 end @@ -159,4 +155,9 @@ class ApplicationController < ActionController::Base redirect_to new_profile_password_path and return end end + + def event_filter + filters = cookies['event_filter'].split(',') if cookies['event_filter'].present? + @event_filter ||= EventFilter.new(filters) + end end diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index f6620b25818660626d37ebb4544df3c5a2005aa7..33b2a5cd7e7f885d42754296f059e1d0abb4c2f1 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -33,9 +33,13 @@ class DashboardController < ApplicationController current_user.owned_projects else current_user.authorized_projects - end.sorted_by_activity + end + + @projects = @projects.where(namespace_id: Group.find_by_name(params[:group])) if params[:group].present? + @projects = @projects.includes(:namespace).sorted_by_activity @labels = current_user.authorized_projects.tags_on(:labels) + @groups = current_user.authorized_groups @projects = @projects.tagged_with(params[:label]) if params[:label].present? @projects = @projects.page(params[:page]).per(30) @@ -66,9 +70,4 @@ class DashboardController < ApplicationController def load_projects @projects = current_user.authorized_projects.sorted_by_activity end - - def event_filter - filters = cookies['event_filter'].split(',') if cookies['event_filter'].present? - @event_filter ||= EventFilter.new(filters) - end end diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 1190dd40b73c0c3c387292132ad5ab23deacfefe..9dc9afe9e67f7e25fe39a24c17287bdb81b4ccd0 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -31,7 +31,9 @@ class GroupsController < ApplicationController end def show - @events = Event.in_projects(project_ids).limit(20).offset(params[:offset] || 0) + @events = Event.in_projects(project_ids) + @events = event_filter.apply_filter(@events) + @events = @events.limit(20).offset(params[:offset] || 0) @last_push = current_user.recent_push respond_to do |format| @@ -87,7 +89,6 @@ class GroupsController < ApplicationController end def destroy - @group.truncate_teams @group.destroy redirect_to root_path, notice: 'Group was removed.' @@ -109,7 +110,7 @@ class GroupsController < ApplicationController # Dont allow unauthorized access to group def authorize_read_group! - unless projects.present? or can?(current_user, :manage_group, @group) + unless projects.present? or can?(current_user, :read_group, @group) return render_404 end end diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index c4ebf0e48896b511b7c75cec48624a90edefa685..7131e0fe181cdc86b3a57b3a2dc66ad7ff198c4f 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -16,35 +16,41 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController end def ldap - # We only find ourselves here if the authentication to LDAP was successful. - @user = User.find_for_ldap_auth(request.env["omniauth.auth"], current_user) - if @user.persisted? - @user.remember_me = true - end - sign_in_and_redirect @user + # We only find ourselves here + # if the authentication to LDAP was successful. + @user = Gitlab::LDAP::User.find_or_create(oauth) + @user.remember_me = true if @user.persisted? + sign_in_and_redirect(@user) end private def handle_omniauth - oauth = request.env['omniauth.auth'] - provider, uid = oauth['provider'], oauth['uid'] - if current_user # Change a logged-in user's authentication method: - current_user.extern_uid = uid - current_user.provider = provider + current_user.extern_uid = oauth['uid'] + current_user.provider = oauth['provider'] current_user.save redirect_to profile_path else - @user = User.find_or_new_for_omniauth(oauth) + @user = Gitlab::OAuth::User.find(oauth) + + # Create user if does not exist + # and allow_single_sign_on is true + if Gitlab.config.omniauth['allow_single_sign_on'] + @user ||= Gitlab::OAuth::User.create(oauth) + end if @user - sign_in_and_redirect @user + sign_in_and_redirect(@user) else flash[:notice] = "There's no such user!" redirect_to new_user_session_path end end end + + def oauth + @oauth ||= request.env['omniauth.auth'] + end end diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb index 6a2d2315c1da5bf5b102888887b79d25bdab0add..bdc501d73bbfeda1f93fc1c49551736f44331ec4 100644 --- a/app/controllers/projects/commit_controller.rb +++ b/app/controllers/projects/commit_controller.rb @@ -18,6 +18,7 @@ class Projects::CommitController < Projects::ApplicationController end @suppress_diff = result[:suppress_diff] + @force_suppress_diff = result[:force_suppress_diff] @note = result[:note] @line_notes = result[:line_notes] diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb index 126a2ea50c9dd821d264d6460b4bf788e43cd83a..d7e660dac224b57c3985a89c82f6fd6ee70d2316 100644 --- a/app/controllers/projects/compare_controller.rb +++ b/app/controllers/projects/compare_controller.rb @@ -15,6 +15,10 @@ class Projects::CompareController < Projects::ApplicationController @diffs = compare.diffs @refs_are_same = compare.same @line_notes = [] + + diff_line_count = Commit::diff_line_count(@diffs) + @suppress_diff = Commit::diff_suppress?(@diffs, diff_line_count) && !params[:force_show_diff] + @force_suppress_diff = Commit::diff_force_suppress?(@diffs, diff_line_count) end def create diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index a7f515ac851d5f1745090fed901b0980aca151a3..e8f845b2d1710a75da654f9dae477d55b3921934 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -91,7 +91,11 @@ class Projects::IssuesController < Projects::ApplicationController protected def issue - @issue ||= @project.issues.find(params[:id]) + @issue ||= begin + @project.issues.find_by_iid!(params[:id]) + rescue ActiveRecord::RecordNotFound + redirect_old + end end def authorize_modify_issue! @@ -109,4 +113,20 @@ class Projects::IssuesController < Projects::ApplicationController def issues_filtered @issues = Issues::ListContext.new(project, current_user, params).execute end + + # Since iids are implemented only in 6.1 + # user may navigate to issue page using old global ids. + # + # To prevent 404 errors we provide a redirect to correct iids until 7.0 release + # + def redirect_old + issue = @project.issues.find_by_id(params[:id]) + + if issue + redirect_to project_issue_path(@project, issue) + return + else + raise ActiveRecord::RecordNotFound.new + end + end end diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index d135cf05b9ed70d98933b6f42e1045c2d0965c85..3bc50b0418f417bc57f143f1cf0c8db65a054c22 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -3,6 +3,7 @@ require 'gitlab/satellite/satellite' class Projects::MergeRequestsController < Projects::ApplicationController before_filter :module_enabled before_filter :merge_request, only: [:edit, :update, :show, :commits, :diffs, :automerge, :automerge_check, :ci_status] + before_filter :closes_issues, only: [:edit, :update, :show, :commits, :diffs] before_filter :validates_merge_request, only: [:show, :diffs] before_filter :define_show_vars, only: [:show, :diffs] @@ -39,6 +40,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController @comments_target = {noteable_type: 'MergeRequest', noteable_id: @merge_request.id} @line_notes = @merge_request.notes.where("line_code is not null") + + diff_line_count = Commit::diff_line_count(@merge_request.diffs) + @suppress_diff = Commit::diff_suppress?(@merge_request.diffs, diff_line_count) && !params[:force_show_diff] + @force_suppress_diff = Commit::diff_force_suppress?(@merge_request.diffs, diff_line_count) end def new @@ -132,7 +137,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController end def merge_request - @merge_request ||= @project.merge_requests.find(params[:id]) + @merge_request ||= @project.merge_requests.find_by_iid!(params[:id]) + end + + def closes_issues + @closes_issues ||= @merge_request.closes_issues end def authorize_modify_merge_request! diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb index f4ee7427d29f831a55f213428fbfeece0b4a4b4e..39cd579cce5b75b38eaa0016e7d8712c9496118a 100644 --- a/app/controllers/projects/milestones_controller.rb +++ b/app/controllers/projects/milestones_controller.rb @@ -81,7 +81,7 @@ class Projects::MilestonesController < Projects::ApplicationController protected def milestone - @milestone ||= @project.milestones.find(params[:id]) + @milestone ||= @project.milestones.find_by_iid!(params[:id]) end def authorize_admin_milestone! diff --git a/app/controllers/projects/network_controller.rb b/app/controllers/projects/network_controller.rb index e4a84f80787027fd899169e30557556e6cdbd01c..9832495c64fb3a1d177b37434d37225d59272d90 100644 --- a/app/controllers/projects/network_controller.rb +++ b/app/controllers/projects/network_controller.rb @@ -8,10 +8,6 @@ class Projects::NetworkController < Projects::ApplicationController before_filter :require_non_empty_project def show - if @options[:q] - @commit = @project.repository.commit(@options[:q]) || @commit - end - respond_to do |format| format.html diff --git a/app/controllers/projects/raw_controller.rb b/app/controllers/projects/raw_controller.rb index 0d35f373e9c7511e10b12c69b1705019f7ff97fd..0c23d411f4c5040bbec86bb4ef73bcce69344e7d 100644 --- a/app/controllers/projects/raw_controller.rb +++ b/app/controllers/projects/raw_controller.rb @@ -11,9 +11,17 @@ class Projects::RawController < Projects::ApplicationController @blob = Gitlab::Git::Blob.new(@repository, @commit.id, @ref, @path) if @blob.exists? + type = if @blob.mime_type =~ /html|javascript/ + 'text/plain; charset=utf-8' + else + @blob.mime_type + end + + headers['X-Content-Type-Options'] = 'nosniff' + send_data( @blob.data, - type: @blob.mime_type, + type: type, disposition: 'inline', filename: @blob.name ) diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 9e88d0b4757511acfd1ed67d3e131e5012640962..23b54ec44a897b5de54005fd65d713f8d6cc00ea 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -55,7 +55,10 @@ class ProjectsController < Projects::ApplicationController def show limit = (params[:limit] || 20).to_i - @events = @project.events.recent.limit(limit).offset(params[:offset] || 0) + + @events = @project.events.recent + @events = event_filter.apply_filter(@events) + @events = @events.limit(limit).offset(params[:offset] || 0) # Ensure project default branch is set if it possible # Normally it defined on push or during creation @@ -104,7 +107,7 @@ class ProjectsController < Projects::ApplicationController def autocomplete_sources @suggestions = { emojis: Emoji.names, - issues: @project.issues.select([:id, :title, :description]), + issues: @project.issues.select([:iid, :title, :description]), members: @project.team.members.sort_by(&:username).map { |user| { username: user.username, name: user.name } } } diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb index 194dfcd4122838a0a51b11e9e04781d5502b4daf..5f18bac82edb8769c17940ed1b5c4338d2c4ec8a 100644 --- a/app/controllers/registrations_controller.rb +++ b/app/controllers/registrations_controller.rb @@ -2,9 +2,6 @@ class RegistrationsController < Devise::RegistrationsController before_filter :signup_enabled? def destroy - if current_user.owned_projects.count > 0 - redirect_to account_profile_path, alert: "Remove projects and groups before removing account." and return - end current_user.destroy respond_to do |format| diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 0a49892ca0acfef21b80919f9e201ab39917910c..4209b081bfa074f7a1871f01f96e2dca26f1066b 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -235,4 +235,11 @@ module ApplicationHelper "Search" end end + + def first_line(str) + lines = str.split("\n") + line = lines.first + line += "..." if lines.size > 1 + line + end end diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index e889b3909685f2e21d72785c6baa66faf30e3ac6..f8f84ff8b62ed638ace978cd56efd4ee6ea59597 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -56,8 +56,9 @@ module CommitsHelper end end - def commit_to_html commit, project - escape_javascript(render 'projects/commits/commit', commit: commit, project: project) unless commit.nil? + def commit_to_html(commit, project, inline = true) + template = inline ? "inline_commit" : "commit" + escape_javascript(render "projects/commits/#{template}", commit: commit, project: project) unless commit.nil? end def diff_line_content(line) diff --git a/app/helpers/compare_helper.rb b/app/helpers/compare_helper.rb new file mode 100644 index 0000000000000000000000000000000000000000..ea2540bf385e31511bc1c438cb5fd14391e4082a --- /dev/null +++ b/app/helpers/compare_helper.rb @@ -0,0 +1,13 @@ +module CompareHelper + def compare_to_mr_button? + params[:from].present? && params[:to].present? && + @repository.branch_names.include?(params[:from]) && + @repository.branch_names.include?(params[:to]) && + params[:from] != params[:to] && + !@refs_are_same + end + + def compare_mr_path + new_project_merge_request_path(@project, merge_request: {source_branch: params[:to], target_branch: params[:from]}) + end +end diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index 8433e72dc24bb3f02826270ef537bd39331214f3..cd8761a6113f6774ec799aec57f683176cad04b4 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -28,7 +28,7 @@ module EventsHelper end content_tag :div, class: "filter_icon #{inactive}" do - link_to dashboard_path, class: 'has_tooltip event_filter_link', id: "#{key}_event_filter", 'data-original-title' => tooltip do + link_to request.path, class: 'has_tooltip event_filter_link', id: "#{key}_event_filter", 'data-original-title' => tooltip do content_tag :i, nil, class: icon_for_event[key] end end @@ -109,7 +109,7 @@ module EventsHelper else link_to event_note_target_path(event) do content_tag :strong do - "#{event.note_target_type} ##{truncate event.note_target_id}" + "#{event.note_target_type} ##{truncate event.note_target_iid}" end end end @@ -123,6 +123,8 @@ module EventsHelper end def event_note(text) - sanitize(markdown(truncate(text, length: 150)), tags: %w(a img b pre p)) + text = first_line(text) + text = truncate(text, length: 150) + sanitize(markdown(text), tags: %w(a img b pre p)) end end diff --git a/app/helpers/graph_helper.rb b/app/helpers/graph_helper.rb index 71a07d6cad1b0c2e1e8550f23be5458ca3d246bc..7cb1b6f8d1aca34e64a96bc870ca06777d2706bf 100644 --- a/app/helpers/graph_helper.rb +++ b/app/helpers/graph_helper.rb @@ -1,7 +1,7 @@ module GraphHelper - def get_refs(commit) + def get_refs(repo, commit) refs = "" - refs += commit.refs.collect{|r|r.name}.join(" ") if commit.refs + refs += commit.ref_names(repo).join(" ") # append note count refs += "[#{@graph.notes[commit.id]}]" if @graph.notes[commit.id] > 0 diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 4f433f3c60d72eff5edf80e1ca527d45057a5c92..5977c9cbae21ad52393e59aaf50a2b6c772e381f 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -37,23 +37,23 @@ module IssuesHelper end end - def url_for_issue(issue_id) + def url_for_issue(issue_iid) return "" if @project.nil? if @project.used_default_issues_tracker? - url = project_issue_url project_id: @project, id: issue_id + url = project_issue_url project_id: @project, id: issue_iid else url = Gitlab.config.issues_tracker[@project.issues_tracker]["issues_url"] - url.gsub(':id', issue_id.to_s) + url.gsub(':id', issue_iid.to_s) .gsub(':project_id', @project.id.to_s) .gsub(':issues_tracker_id', @project.issues_tracker_id.to_s) end end - def title_for_issue(issue_id) + def title_for_issue(issue_iid) return "" if @project.nil? - if @project.used_default_issues_tracker? && issue = @project.issues.where(id: issue_id).first + if @project.used_default_issues_tracker? && issue = @project.issues.where(iid: issue_iid).first issue.title else "" diff --git a/app/helpers/profile_helper.rb b/app/helpers/profile_helper.rb index 80d67009f597dc9b2a2b0f8eb10f700dbdd8b12f..88d9f184d0e50f879113df5a6c9a26ca9806c1d3 100644 --- a/app/helpers/profile_helper.rb +++ b/app/helpers/profile_helper.rb @@ -4,4 +4,16 @@ module ProfileHelper 'active' end end + + def show_profile_username_tab? + current_user.can_change_username? + end + + def show_profile_social_tab? + Gitlab.config.omniauth.enabled && !current_user.ldap_user? + end + + def show_profile_remove_tab? + Gitlab.config.gitlab.signup_enabled && !current_user.ldap_user? + end end diff --git a/app/mailers/emails/groups.rb b/app/mailers/emails/groups.rb new file mode 100644 index 0000000000000000000000000000000000000000..2e9d28981e37f2f987ae5144ef7c74d88ce5e081 --- /dev/null +++ b/app/mailers/emails/groups.rb @@ -0,0 +1,11 @@ +module Emails + module Groups + def group_access_granted_email(user_group_id) + @membership = UsersGroup.find(user_group_id) + @group = @membership.group + + mail(to: @membership.user.email, + subject: subject("access to group was granted")) + end + end +end diff --git a/app/mailers/emails/issues.rb b/app/mailers/emails/issues.rb index c1112a98f92c6d05dccce7ea34736d1b50251e3f..6eda88c792115ac34e344529c65fe781bc33dbbe 100644 --- a/app/mailers/emails/issues.rb +++ b/app/mailers/emails/issues.rb @@ -3,14 +3,14 @@ module Emails def new_issue_email(recipient_id, issue_id) @issue = Issue.find(issue_id) @project = @issue.project - mail(to: recipient(recipient_id), subject: subject("new issue ##{@issue.id}", @issue.title)) + mail(to: recipient(recipient_id), subject: subject("new issue ##{@issue.iid}", @issue.title)) end def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id) @issue = Issue.find(issue_id) @previous_assignee = User.find_by_id(previous_assignee_id) if previous_assignee_id @project = @issue.project - mail(to: recipient(recipient_id), subject: subject("changed issue ##{@issue.id}", @issue.title)) + mail(to: recipient(recipient_id), subject: subject("changed issue ##{@issue.iid}", @issue.title)) end def closed_issue_email(recipient_id, issue_id, updated_by_user_id) @@ -18,7 +18,7 @@ module Emails @project = @issue.project @updated_by = User.find updated_by_user_id mail(to: recipient(recipient_id), - subject: subject("Closed issue ##{@issue.id}", @issue.title)) + subject: subject("Closed issue ##{@issue.iid}", @issue.title)) end def issue_status_changed_email(recipient_id, issue_id, status, updated_by_user_id) @@ -27,7 +27,7 @@ module Emails @project = @issue.project @updated_by = User.find updated_by_user_id mail(to: recipient(recipient_id), - subject: subject("changed issue ##{@issue.id}", @issue.title)) + subject: subject("changed issue ##{@issue.iid}", @issue.title)) end end end diff --git a/app/mailers/emails/merge_requests.rb b/app/mailers/emails/merge_requests.rb index cf6950966e128b2135889c02e01202fbc733ed71..57c1925fa47e786d45bf899300134111ff900524 100644 --- a/app/mailers/emails/merge_requests.rb +++ b/app/mailers/emails/merge_requests.rb @@ -2,24 +2,24 @@ module Emails module MergeRequests def new_merge_request_email(recipient_id, merge_request_id) @merge_request = MergeRequest.find(merge_request_id) - mail(to: recipient(recipient_id), subject: subject("new merge request !#{@merge_request.id}", @merge_request.title)) + mail(to: recipient(recipient_id), subject: subject("new merge request !#{@merge_request.iid}", @merge_request.title)) end def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id) @merge_request = MergeRequest.find(merge_request_id) @previous_assignee = User.find_by_id(previous_assignee_id) if previous_assignee_id - mail(to: recipient(recipient_id), subject: subject("changed merge request !#{@merge_request.id}", @merge_request.title)) + mail(to: recipient(recipient_id), subject: subject("changed merge request !#{@merge_request.iid}", @merge_request.title)) end def closed_merge_request_email(recipient_id, merge_request_id, updated_by_user_id) @merge_request = MergeRequest.find(merge_request_id) @updated_by = User.find updated_by_user_id - mail(to: recipient(recipient_id), subject: subject("Closed merge request !#{@merge_request.id}", @merge_request.title)) + mail(to: recipient(recipient_id), subject: subject("Closed merge request !#{@merge_request.iid}", @merge_request.title)) end def merged_merge_request_email(recipient_id, merge_request_id) @merge_request = MergeRequest.find(merge_request_id) - mail(to: recipient(recipient_id), subject: subject("Accepted merge request !#{@merge_request.id}", @merge_request.title)) + mail(to: recipient(recipient_id), subject: subject("Accepted merge request !#{@merge_request.iid}", @merge_request.title)) end end diff --git a/app/mailers/emails/notes.rb b/app/mailers/emails/notes.rb index 769b6e0b8611786458b18b4118f59d69dc8b6295..761b4c8161f1be03fb679676729256e74d0a2341 100644 --- a/app/mailers/emails/notes.rb +++ b/app/mailers/emails/notes.rb @@ -11,14 +11,14 @@ module Emails @note = Note.find(note_id) @issue = @note.noteable @project = @note.project - mail(to: recipient(recipient_id), subject: subject("note for issue ##{@issue.id}")) + mail(to: recipient(recipient_id), subject: subject("note for issue ##{@issue.iid}")) end def note_merge_request_email(recipient_id, note_id) @note = Note.find(note_id) @merge_request = @note.noteable @project = @note.project - mail(to: recipient(recipient_id), subject: subject("note for merge request !#{@merge_request.id}")) + mail(to: recipient(recipient_id), subject: subject("note for merge request !#{@merge_request.iid}")) end def note_wall_email(recipient_id, note_id) diff --git a/app/mailers/emails/profile.rb b/app/mailers/emails/profile.rb new file mode 100644 index 0000000000000000000000000000000000000000..bcd44f9476c2150ead37af7c0aa81f3743110b1e --- /dev/null +++ b/app/mailers/emails/profile.rb @@ -0,0 +1,15 @@ +module Emails + module Profile + def new_user_email(user_id, password) + @user = User.find(user_id) + @password = password + mail(to: @user.email, subject: subject("Account was created for you")) + end + + def new_ssh_key_email(key_id) + @key = Key.find(key_id) + @user = @key.user + mail(to: @user.email, subject: subject("SSH key was added to your account")) + end + end +end diff --git a/app/mailers/emails/projects.rb b/app/mailers/emails/projects.rb index 1ad7ca588bd2cf5fec24654aa23c07a8a709e848..4d5fe9ef6142858ca51797ff56d3e9d5c7d9b05e 100644 --- a/app/mailers/emails/projects.rb +++ b/app/mailers/emails/projects.rb @@ -7,7 +7,6 @@ module Emails subject: subject("access to project was granted")) end - def project_was_moved_email(project_id, user_id) @user = User.find user_id @project = Project.find project_id diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb index 7890ca7793baa86258ba42690558aae2f98f8af8..2f7be00c33ed48e37d18fffc33952ee87f81a7b2 100644 --- a/app/mailers/notify.rb +++ b/app/mailers/notify.rb @@ -3,6 +3,8 @@ class Notify < ActionMailer::Base include Emails::MergeRequests include Emails::Notes include Emails::Projects + include Emails::Profile + include Emails::Groups add_template_helper ApplicationHelper add_template_helper GitlabMarkdownHelper @@ -20,18 +22,6 @@ class Notify < ActionMailer::Base delay_for(2.seconds) end - def new_user_email(user_id, password) - @user = User.find(user_id) - @password = password - mail(to: @user.email, subject: subject("Account was created for you")) - end - - def new_ssh_key_email(key_id) - @key = Key.find(key_id) - @user = @key.user - mail(to: @user.email, subject: subject("SSH key was added to your account")) - end - private # Look up a User by their ID and return their email address diff --git a/app/models/ability.rb b/app/models/ability.rb index 0b77564adc65e05309173b5439e2143aee171d0a..8335829f91967a67db73c295286eba37e09976da 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -2,6 +2,7 @@ class Ability class << self def allowed(user, subject) return [] unless user.kind_of?(User) + return [] if user.blocked? case subject.class.name when "Project" then project_abilities(user, subject) @@ -134,6 +135,10 @@ class Ability def group_abilities user, group rules = [] + if group.users.include?(user) + rules << :read_group + end + # Only group owner and administrators can manage group if group.owners.include?(user) || user.admin? rules << [ diff --git a/app/models/commit.rb b/app/models/commit.rb index da80c2940ff7bfae1bdb45d15ba16a22aca8e010..dd1f980187852f2fc8131e57c2a33e93442b8297 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -2,16 +2,45 @@ class Commit include ActiveModel::Conversion include StaticModel extend ActiveModel::Naming + include Mentionable - # Safe amount of files with diffs in one commit to render + attr_mentionable :safe_message + + # Safe amount of changes (files and lines) in one commit to render # Used to prevent 500 error on huge commits by suppressing diff # - DIFF_SAFE_SIZE = 100 + # User can force display of diff above this size + DIFF_SAFE_FILES = 100 + DIFF_SAFE_LINES = 5000 + # Commits above this size will not be rendered in HTML + DIFF_HARD_LIMIT_FILES = 500 + DIFF_HARD_LIMIT_LINES = 10000 def self.decorate(commits) commits.map { |c| self.new(c) } end + # Calculate number of lines to render for diffs + def self.diff_line_count(diffs) + diffs.reduce(0){|sum, d| sum + d.diff.lines.count} + end + + def self.diff_suppress?(diffs, line_count = nil) + # optimize - check file count first + return true if diffs.size > DIFF_SAFE_FILES + + line_count ||= Commit::diff_line_count(diffs) + line_count > DIFF_SAFE_LINES + end + + def self.diff_force_suppress?(diffs, line_count = nil) + # optimize - check file count first + return true if diffs.size > DIFF_HARD_LIMIT_FILES + + line_count ||= Commit::diff_line_count(diffs) + line_count > DIFF_HARD_LIMIT_LINES + end + attr_accessor :raw def initialize(raw_commit) @@ -24,6 +53,19 @@ class Commit @raw.id end + def diff_line_count + @diff_line_count ||= Commit::diff_line_count(self.diffs) + @diff_line_count + end + + def diff_suppress? + Commit::diff_suppress?(self.diffs, diff_line_count) + end + + def diff_force_suppress? + Commit::diff_force_suppress?(self.diffs, diff_line_count) + end + # Returns a string describing the commit for use in a link title # # Example @@ -65,6 +107,29 @@ class Commit end end + # Regular expression that identifies commit message clauses that trigger issue closing. + def issue_closing_regex + @issue_closing_regex ||= Regexp.new(Gitlab.config.gitlab.issue_closing_pattern) + end + + # Discover issues should be closed when this commit is pushed to a project's + # default branch. + def closes_issues project + md = issue_closing_regex.match(safe_message) + if md + extractor = Gitlab::ReferenceExtractor.new + extractor.analyze(md[0]) + extractor.issues_for(project) + else + [] + end + end + + # Mentionable override. + def gfm_reference + "commit #{sha[0..5]}" + end + def method_missing(m, *args, &block) @raw.send(m, *args, &block) end diff --git a/app/models/concerns/internal_id.rb b/app/models/concerns/internal_id.rb new file mode 100644 index 0000000000000000000000000000000000000000..821ed54fb987568a3fd41c638709127e1fefccb1 --- /dev/null +++ b/app/models/concerns/internal_id.rb @@ -0,0 +1,17 @@ +module InternalId + extend ActiveSupport::Concern + + included do + validate :set_iid, on: :create + validates :iid, presence: true, numericality: true + end + + def set_iid + max_iid = project.send(self.class.name.tableize).maximum(:iid) + self.iid = max_iid.to_i + 1 + end + + def to_param + iid.to_s + end +end diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 91fb323825d250e5784c23d9bd40aee69adb75e2..7f820f950b02b02d601eca4779ad4b586009945b 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -24,6 +24,7 @@ module Issuable scope :unassigned, -> { where("assignee_id IS NULL") } scope :of_projects, ->(ids) { where(project_id: ids) } + delegate :name, :email, to: :author, @@ -36,6 +37,8 @@ module Issuable prefix: true attr_accessor :author_id_of_changes + + attr_mentionable :title, :description end module ClassMethods diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb index f22070f85043f97b6eef5b10c4884eb8698c969e..5858fe1bb6fd2c813df588d984f51fdd64131f54 100644 --- a/app/models/concerns/mentionable.rb +++ b/app/models/concerns/mentionable.rb @@ -1,12 +1,47 @@ # == Mentionable concern # -# Contains common functionality shared between Issues and Notes +# Contains functionality related to objects that can mention Users, Issues, MergeRequests, or Commits by +# GFM references. # -# Used by Issue, Note +# Used by Issue, Note, MergeRequest, and Commit. # module Mentionable extend ActiveSupport::Concern + module ClassMethods + # Indicate which attributes of the Mentionable to search for GFM references. + def attr_mentionable *attrs + mentionable_attrs.concat(attrs.map(&:to_s)) + end + + # Accessor for attributes marked mentionable. + def mentionable_attrs + @mentionable_attrs ||= [] + end + end + + # Generate a GFM back-reference that will construct a link back to this Mentionable when rendered. Must + # be overridden if this model object can be referenced directly by GFM notation. + def gfm_reference + raise NotImplementedError.new("#{self.class} does not implement #gfm_reference") + end + + # Construct a String that contains possible GFM references. + def mentionable_text + self.class.mentionable_attrs.map { |attr| send(attr) || '' }.join + end + + # The GFM reference to this Mentionable, which shouldn't be included in its #references. + def local_reference + self + end + + # Determine whether or not a cross-reference Note has already been created between this Mentionable and + # the specified target. + def has_mentioned? target + Note.cross_reference_exists?(target, local_reference) + end + def mentioned_users users = [] return users if mentionable_text.blank? @@ -15,7 +50,7 @@ module Mentionable matches.each do |match| identifier = match.delete "@" if has_project - id = project.users_projects.joins(:user).where(users: { username: identifier }).pluck(:user_id).first + id = project.team.members.find { |u| u.username == identifier }.try(:id) else id = User.where(username: identifier).pluck(:id).first end @@ -24,14 +59,39 @@ module Mentionable users.uniq end - def mentionable_text - if self.class == Issue - description - elsif self.class == Note - note - else - nil + # Extract GFM references to other Mentionables from this Mentionable. Always excludes its #local_reference. + def references p = project, text = mentionable_text + return [] if text.blank? + ext = Gitlab::ReferenceExtractor.new + ext.analyze(text) + (ext.issues_for(p) + ext.merge_requests_for(p) + ext.commits_for(p)).uniq - [local_reference] + end + + # Create a cross-reference Note for each GFM reference to another Mentionable found in +mentionable_text+. + def create_cross_references! p = project, a = author, without = [] + refs = references(p) - without + refs.each do |ref| + Note.create_cross_reference_note(ref, local_reference, a, p) end end + # If the mentionable_text field is about to change, locate any *added* references and create cross references for + # them. Invoke from an observer's #before_save implementation. + def notice_added_references p = project, a = author + ch = changed_attributes + original, mentionable_changed = "", false + self.class.mentionable_attrs.each do |attr| + if ch[attr] + original << ch[attr] + mentionable_changed = true + end + end + + # Only proceed if the saved changes actually include a chance to an attr_mentionable field. + return unless mentionable_changed + + preexisting = references(p, original) + create_cross_references!(p, a, preexisting) + end + end diff --git a/app/models/deploy_key.rb b/app/models/deploy_key.rb index f983198085cd0c2dc19b43f5690a52c7a87d3a1e..47aeb93a419510ca1d7a1c2704a30c8378609fff 100644 --- a/app/models/deploy_key.rb +++ b/app/models/deploy_key.rb @@ -2,14 +2,14 @@ # # Table name: keys # -# id :integer not null, primary key -# user_id :integer -# created_at :datetime -# updated_at :datetime -# key :text -# title :string(255) -# identifier :string(255) -# type :string(255) +# id :integer not null, primary key +# user_id :integer +# created_at :datetime not null +# updated_at :datetime not null +# key :text +# title :string(255) +# type :string(255) +# fingerprint :string(255) # class DeployKey < Key diff --git a/app/models/deprecated/user_team.rb b/app/models/deprecated/user_team.rb deleted file mode 100644 index 822884297d46515ddfcd227c403cd96250b9ee77..0000000000000000000000000000000000000000 --- a/app/models/deprecated/user_team.rb +++ /dev/null @@ -1,118 +0,0 @@ -# Will be removed in 6.1 with tables -# -# == Schema Information -# -# Table name: user_teams -# -# id :integer not null, primary key -# name :string(255) -# path :string(255) -# owner_id :integer -# created_at :datetime not null -# updated_at :datetime not null -# description :string(255) default(""), not null -# - -class UserTeam < ActiveRecord::Base - attr_accessible :name, :description, :owner_id, :path - - belongs_to :owner, class_name: User - - has_many :user_team_project_relationships, dependent: :destroy - has_many :user_team_user_relationships, dependent: :destroy - - has_many :projects, through: :user_team_project_relationships - has_many :members, through: :user_team_user_relationships, source: :user - - validates :owner, presence: true - validates :name, presence: true, uniqueness: true, - length: { within: 0..255 }, - format: { with: Gitlab::Regex.name_regex, - message: "only letters, digits, spaces & '_' '-' '.' allowed." } - validates :description, length: { within: 0..255 } - validates :path, uniqueness: true, presence: true, length: { within: 1..255 }, - format: { with: Gitlab::Regex.path_regex, - message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" } - - scope :with_member, ->(user){ joins(:user_team_user_relationships).where(user_team_user_relationships: {user_id: user.id}) } - scope :with_project, ->(project){ joins(:user_team_project_relationships).where(user_team_project_relationships: {project_id: project})} - scope :without_project, ->(project){ where("user_teams.id NOT IN (:ids)", ids: (a = with_project(project); a.blank? ? 0 : a))} - scope :created_by, ->(user){ where(owner_id: user) } - - class << self - def search query - where("name LIKE :query OR path LIKE :query", query: "%#{query}%") - end - - def global_id - 'GLN' - end - - def access_roles - UsersProject.access_roles - end - end - - def to_param - path - end - - def assign_to_projects(projects, access) - projects.each do |project| - assign_to_project(project, access) - end - end - - def assign_to_project(project, access) - Gitlab::UserTeamManager.assign(self, project, access) - end - - def resign_from_project(project) - Gitlab::UserTeamManager.resign(self, project) - end - - def add_members(users, access, group_admin) - # reject existing users - users.reject! { |id| member_ids.include?(id.to_i) } - - users.each do |user| - add_member(user, access, group_admin) - end - end - - def add_member(user, access, group_admin) - Gitlab::UserTeamManager.add_member_into_team(self, user, access, group_admin) - end - - def remove_member(user) - Gitlab::UserTeamManager.remove_member_from_team(self, user) - end - - def update_membership(user, options) - Gitlab::UserTeamManager.update_team_user_membership(self, user, options) - end - - def update_project_access(project, permission) - Gitlab::UserTeamManager.update_project_greates_access(self, project, permission) - end - - def max_project_access(project) - user_team_project_relationships.find_by_project_id(project).greatest_access - end - - def human_max_project_access(project) - self.class.access_roles.invert[max_project_access(project)] - end - - def default_projects_access(member) - user_team_user_relationships.find_by_user_id(member).permission - end - - def human_default_projects_access(member) - self.class.access_roles.invert[default_projects_access(member)] - end - - def admin?(member) - user_team_user_relationships.with_user(member).first.try(:group_admin?) - end -end diff --git a/app/models/deprecated/user_team_project_relationship.rb b/app/models/deprecated/user_team_project_relationship.rb deleted file mode 100644 index e93223f3e24eda517b342e2e9eaf4ed09fe79604..0000000000000000000000000000000000000000 --- a/app/models/deprecated/user_team_project_relationship.rb +++ /dev/null @@ -1,46 +0,0 @@ -# Will be removed in 6.1 with tables -# -# == Schema Information -# -# Table name: user_team_project_relationships -# -# id :integer not null, primary key -# project_id :integer -# user_team_id :integer -# greatest_access :integer -# created_at :datetime not null -# updated_at :datetime not null -# - -class UserTeamProjectRelationship < ActiveRecord::Base - attr_accessible :greatest_access, :project_id, :user_team_id - - belongs_to :user_team - belongs_to :project - - validates :project, presence: true - validates :user_team, presence: true - validate :check_greatest_access - - scope :with_project, ->(project){ where(project_id: project.id) } - - def team_name - user_team.name - end - - def human_max_access - UserTeam.access_roles.key(greatest_access) - end - - private - - def check_greatest_access - errors.add(:base, :incorrect_access_code) unless correct_access? - end - - def correct_access? - return false if greatest_access.blank? - return true if UsersProject.access_roles.has_value?(greatest_access) - false - end -end diff --git a/app/models/deprecated/user_team_user_relationship.rb b/app/models/deprecated/user_team_user_relationship.rb deleted file mode 100644 index ae4c789994a2dcff24d8e3d3e2cd5a1b1c62cb5f..0000000000000000000000000000000000000000 --- a/app/models/deprecated/user_team_user_relationship.rb +++ /dev/null @@ -1,34 +0,0 @@ -# Will be removed in 6.1 with tables -# -# == Schema Information -# -# Table name: user_team_user_relationships -# -# id :integer not null, primary key -# user_id :integer -# user_team_id :integer -# group_admin :boolean -# permission :integer -# created_at :datetime not null -# updated_at :datetime not null -# - -class UserTeamUserRelationship < ActiveRecord::Base - attr_accessible :group_admin, :permission, :user_id, :user_team_id - - belongs_to :user_team - belongs_to :user - - validates :user_team, presence: true - validates :user, presence: true - - scope :with_user, ->(user) { where(user_id: user.id) } - - def user_name - user.name - end - - def access_human - UsersProject.access_roles.invert[permission] - end -end diff --git a/app/models/event.rb b/app/models/event.rb index 5c7c5be4190a7e995389b7cf57afb812097cb265..095a06c956b82dddb5279357c1d4e5562a86cdaa 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -256,6 +256,10 @@ class Event < ActiveRecord::Base target.commit_id end + def target_iid + target.respond_to?(:iid) ? target.iid : target_id + end + def note_short_commit_id note_commit_id[0..8] end @@ -280,6 +284,14 @@ class Event < ActiveRecord::Base end end + def note_target_iid + if note_target.respond_to?(:iid) + note_target.iid + else + note_target_id + end.to_s + end + def wall_note? target.noteable_type.blank? end diff --git a/app/models/group.rb b/app/models/group.rb index 1c8eb98e0f270fa28d7b9f23cd11991eee867586..fce8d71217bb08c0af100d634a6b2c84b85cf287 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -23,7 +23,7 @@ class Group < Namespace end def owners - @owners ||= (users_groups.owners.map(&:user) << owner) + @owners ||= (users_groups.owners.map(&:user) << owner).uniq end def add_users(user_ids, group_access) diff --git a/app/models/issue.rb b/app/models/issue.rb index ecb881ab59756cca072b742bccf16715c6c5d3bc..f3ec322126facfbbac0bdd9ef6763afd82e03a46 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -7,18 +7,19 @@ # assignee_id :integer # author_id :integer # project_id :integer -# created_at :datetime -# updated_at :datetime +# created_at :datetime not null +# updated_at :datetime not null # position :integer default(0) # branch_name :string(255) # description :text # milestone_id :integer # state :string(255) +# iid :integer # class Issue < ActiveRecord::Base - include Issuable + include InternalId belongs_to :project validates :project, presence: true @@ -55,4 +56,10 @@ class Issue < ActiveRecord::Base # Both open and reopened issues should be listed as opened scope :opened, -> { with_state(:opened, :reopened) } + + # Mentionable overrides. + + def gfm_reference + "issue ##{iid}" + end end diff --git a/app/models/key.rb b/app/models/key.rb index 902a7c5911654acee79cb98d4d855f15299d8f0c..79f7bbd25901e8334750839545a9bd021f0e4c83 100644 --- a/app/models/key.rb +++ b/app/models/key.rb @@ -2,14 +2,14 @@ # # Table name: keys # -# id :integer not null, primary key -# user_id :integer -# created_at :datetime -# updated_at :datetime -# key :text -# title :string(255) -# identifier :string(255) -# type :string(255) +# id :integer not null, primary key +# user_id :integer +# created_at :datetime not null +# updated_at :datetime not null +# key :text +# title :string(255) +# type :string(255) +# fingerprint :string(255) # require 'digest/md5' diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index b7df2e40a164ddf7c898cc007b31f5e427e6857a..7f367588b23b14955ec0158257221a569e04fe86 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -2,35 +2,35 @@ # # Table name: merge_requests # -# id :integer not null, primary key -# target_project_id :integer not null -# target_branch :string(255) not null -# source_project_id :integer not null -# source_branch :string(255) not null -# author_id :integer -# assignee_id :integer -# title :string(255) -# created_at :datetime -# updated_at :datetime -# st_commits :text(2147483647) -# st_diffs :text(2147483647) -# milestone_id :integer -# state :string(255) -# merge_status :string(255) +# id :integer not null, primary key +# target_branch :string(255) not null +# source_branch :string(255) not null +# source_project_id :integer not null +# author_id :integer +# assignee_id :integer +# title :string(255) +# created_at :datetime not null +# updated_at :datetime not null +# st_commits :text(2147483647) +# st_diffs :text(2147483647) +# milestone_id :integer +# state :string(255) +# merge_status :string(255) +# target_project_id :integer not null +# iid :integer # require Rails.root.join("app/models/commit") require Rails.root.join("lib/static_model") class MergeRequest < ActiveRecord::Base - include Issuable + include InternalId belongs_to :target_project, foreign_key: :target_project_id, class_name: "Project" belongs_to :source_project, foreign_key: :source_project_id, class_name: "Project" - attr_accessible :title, :assignee_id, :source_project_id, :source_branch, :target_project_id, :target_branch, :milestone_id, :author_id_of_changes, :state_event - + attr_accessible :title, :assignee_id, :source_project_id, :source_branch, :target_project_id, :target_branch, :milestone_id, :author_id_of_changes, :state_event, :description attr_accessor :should_remove_source_branch @@ -250,6 +250,24 @@ class MergeRequest < ActiveRecord::Base (source_project.root_ref? source_branch) || for_fork? end + def project + target_project + end + + # Return the set of issues that will be closed if this merge request is accepted. + def closes_issues + if target_branch == project.default_branch + unmerged_commits.map { |c| c.closes_issues(project) }.flatten.uniq.sort_by(&:id) + else + [] + end + end + + # Mentionable override. + def gfm_reference + "merge request !#{iid}" + end + private def dump_commits(commits) diff --git a/app/models/milestone.rb b/app/models/milestone.rb index 023b8ddf04d22bbfed12b917cf7819d08bfb1e29..1a73fa71e4868ad64cd71a0465d52d3a3db2eafb 100644 --- a/app/models/milestone.rb +++ b/app/models/milestone.rb @@ -10,9 +10,12 @@ # created_at :datetime not null # updated_at :datetime not null # state :string(255) +# iid :integer # class Milestone < ActiveRecord::Base + include InternalId + attr_accessible :title, :description, :due_date, :state_event, :author_id_of_changes attr_accessor :author_id_of_changes diff --git a/app/models/network/commit.rb b/app/models/network/commit.rb index 3cd0c015fa021edb2626836e820ddad609a691c8..e31adcebbe7346041d3aeec1dd6e7e40de2eec80 100644 --- a/app/models/network/commit.rb +++ b/app/models/network/commit.rb @@ -4,15 +4,13 @@ module Network class Commit include ActionView::Helpers::TagHelper - attr_reader :refs attr_accessor :time, :spaces, :parent_spaces - def initialize(raw_commit, refs) - @commit = Gitlab::Git::Commit.new(raw_commit) + def initialize(raw_commit) + @commit = raw_commit @time = -1 @spaces = [] @parent_spaces = [] - @refs = refs || [] end def method_missing(m, *args, &block) diff --git a/app/models/network/graph.rb b/app/models/network/graph.rb index 1d21e96369a41452132b9df21679570a92cca586..424819f35018ece45a3ee8c185078c020b7d23ba 100644 --- a/app/models/network/graph.rb +++ b/app/models/network/graph.rb @@ -1,8 +1,6 @@ -require "grit" - module Network class Graph - attr_reader :days, :commits, :map, :notes + attr_reader :days, :commits, :map, :notes, :repo def self.max_count @max_count ||= 650 @@ -13,7 +11,7 @@ module Network @ref = ref @commit = commit @filter_ref = filter_ref - @repo = project.repo + @repo = project.repository @commits = collect_commits @days = index_commits @@ -33,11 +31,9 @@ module Network # Get commits from repository # def collect_commits - refs_cache = build_refs_cache - find_commits(count_to_display_commit_in_center).map do |commit| # Decorate with app/model/network/commit.rb - Network::Commit.new(commit, refs_cache[commit.id]) + Network::Commit.new(commit) end end @@ -103,14 +99,13 @@ module Network def find_commits(skip = 0) opts = { - date_order: true, max_count: self.class.max_count, skip: skip } - ref = @ref if @filter_ref + opts[:ref] = @commit.id if @filter_ref - Grit::Commit.find_all(@repo, ref, opts) + @repo.find_commits(opts) end def commits_sort_by_ref @@ -126,15 +121,7 @@ module Network end def include_ref?(commit) - heads = commit.refs.select do |ref| - ref.is_a?(Grit::Head) or ref.is_a?(Grit::Remote) or ref.is_a?(Grit::Tag) - end - - heads.map! do |head| - head.name - end - - heads.include?(@ref) + commit.ref_names(@repo).include?(@ref) end def find_free_parent_spaces(commit) @@ -282,14 +269,5 @@ module Network leaves.push(commit) end end - - def build_refs_cache - refs_cache = {} - @repo.refs.each do |ref| - refs_cache[ref.commit.id] = [] unless refs_cache.include?(ref.commit.id) - refs_cache[ref.commit.id] << ref - end - refs_cache - end end end diff --git a/app/models/note.rb b/app/models/note.rb index c0bf79237c19194465e168b60c17f3ed2557a335..e819a5516b5c926c1ff3cca552bd2b574f19941a 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -6,13 +6,14 @@ # note :text # noteable_type :string(255) # author_id :integer -# created_at :datetime -# updated_at :datetime +# created_at :datetime not null +# updated_at :datetime not null # project_id :integer # attachment :string(255) # line_code :string(255) # commit_id :string(255) # noteable_id :integer +# st_diff :text # require 'carrierwave/orm/activerecord' @@ -23,6 +24,7 @@ class Note < ActiveRecord::Base attr_accessible :note, :noteable, :noteable_id, :noteable_type, :project_id, :attachment, :line_code, :commit_id + attr_mentionable :note belongs_to :project belongs_to :noteable, polymorphic: true @@ -53,15 +55,36 @@ class Note < ActiveRecord::Base serialize :st_diff before_create :set_diff, if: ->(n) { n.line_code.present? } - def self.create_status_change_note(noteable, project, author, status) + def self.create_status_change_note(noteable, project, author, status, source) + body = "_Status changed to #{status}#{' by ' + source.gfm_reference if source}_" + + create({ + noteable: noteable, + project: project, + author: author, + note: body, + system: true + }, without_protection: true) + end + + # +noteable+ was referenced from +mentioner+, by including GFM in either +mentioner+'s description or an associated Note. + # Create a system Note associated with +noteable+ with a GFM back-reference to +mentioner+. + def self.create_cross_reference_note(noteable, mentioner, author, project) create({ noteable: noteable, + commit_id: (noteable.sha if noteable.respond_to? :sha), project: project, author: author, - note: "_Status changed to #{status}_" + note: "_mentioned in #{mentioner.gfm_reference}_", + system: true }, without_protection: true) end + # Determine whether or not a cross-reference note already exists. + def self.cross_reference_exists?(noteable, mentioner) + where(noteable_id: noteable.id, system: true, note: "_mentioned in #{mentioner.gfm_reference}_").any? + end + def commit_author @commit_author ||= project.users.find_by_email(noteable.author_email) || @@ -190,6 +213,16 @@ class Note < ActiveRecord::Base for_issue? || (for_merge_request? && !for_diff_line?) end + # Mentionable override. + def gfm_reference + noteable.gfm_reference + end + + # Mentionable override. + def local_reference + noteable + end + def noteable_type_name if noteable_type.present? noteable_type.downcase diff --git a/app/models/personal_snippet.rb b/app/models/personal_snippet.rb index a3c0d201ee578b2a47ad737986af3dc3be2575dc..ef2000ad05ecbf992f3d217add36506b386ef010 100644 --- a/app/models/personal_snippet.rb +++ b/app/models/personal_snippet.rb @@ -4,11 +4,11 @@ # # id :integer not null, primary key # title :string(255) -# content :text +# content :text(2147483647) # author_id :integer not null # project_id :integer -# created_at :datetime -# updated_at :datetime +# created_at :datetime not null +# updated_at :datetime not null # file_name :string(255) # expires_at :datetime # private :boolean default(TRUE), not null diff --git a/app/models/project.rb b/app/models/project.rb index d8fa6cbe0681071f6cf7c4831e76f52524b0a0e3..6c4f3075e1c0cff2473feb852ee224c1316044e1 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -6,8 +6,8 @@ # name :string(255) # path :string(255) # description :text -# created_at :datetime -# updated_at :datetime +# created_at :datetime not null +# updated_at :datetime not null # creator_id :integer # default_branch :string(255) # issues_enabled :boolean default(TRUE), not null @@ -21,6 +21,7 @@ # snippets_enabled :boolean default(TRUE), not null # last_activity_at :datetime # imported :boolean default(FALSE), not null +# import_url :string(255) # require "grit" @@ -133,10 +134,6 @@ class Project < ActiveRecord::Base where(path: id, namespace_id: nil).last end end - - def access_options - UsersProject.access_roles - end end def team @@ -201,7 +198,7 @@ class Project < ActiveRecord::Base def issue_exists?(issue_id) if used_default_issues_tracker? - self.issues.where(id: issue_id).first.present? + self.issues.where(iid: issue_id).first.present? else true end @@ -399,11 +396,6 @@ class Project < ActiveRecord::Base http_url = [Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('') end - def project_access_human(member) - project_user_relation = self.users_projects.find_by_user_id(member.id) - self.class.access_options.key(project_user_relation.project_access) - end - # Check if current branch name is marked as protected in the system def protected_branch? branch_name protected_branches_names.include?(branch_name) @@ -429,7 +421,9 @@ class Project < ActiveRecord::Base begin gitlab_shell.mv_repository("#{old_path_with_namespace}.wiki", "#{new_path_with_namespace}.wiki") gitlab_shell.rm_satellites(old_path_with_namespace) + ensure_satellite_exists send_move_instructions + reset_events_cache rescue # Returning false does not rollback after_* transaction but gives # us information about failing some of tasks @@ -441,4 +435,19 @@ class Project < ActiveRecord::Base raise Exception.new('repository cannot be renamed') end end + + # Reset events cache related to this project + # + # Since we do cache @event we need to reset cache in special cases: + # * when project was moved + # * when project was renamed + # Events cache stored like events/23-20130109142513. + # The cache key includes updated_at timestamp. + # Thus it will automatically generate a new fragment + # when the event is updated because the key changes. + def reset_events_cache + Event.where(project_id: self.id). + order('id DESC').limit(100). + update_all(updated_at: Time.now) + end end diff --git a/app/models/project_hook.rb b/app/models/project_hook.rb index b82e1ffd4ab67afbb4a7bca4d0159c9bac16d9c0..2576fc979d4b8f6798d92358c19d35315cd30396 100644 --- a/app/models/project_hook.rb +++ b/app/models/project_hook.rb @@ -5,8 +5,8 @@ # id :integer not null, primary key # url :string(255) # project_id :integer -# created_at :datetime -# updated_at :datetime +# created_at :datetime not null +# updated_at :datetime not null # type :string(255) default("ProjectHook") # service_id :integer # diff --git a/app/models/project_snippet.rb b/app/models/project_snippet.rb index 14c8804642363e7481891ba45d24cbd4ab484b8e..f38aa07059cb369621d4a998699d7a227a3d6cbf 100644 --- a/app/models/project_snippet.rb +++ b/app/models/project_snippet.rb @@ -4,11 +4,11 @@ # # id :integer not null, primary key # title :string(255) -# content :text +# content :text(2147483647) # author_id :integer not null # project_id :integer -# created_at :datetime -# updated_at :datetime +# created_at :datetime not null +# updated_at :datetime not null # file_name :string(255) # expires_at :datetime # private :boolean default(TRUE), not null diff --git a/app/models/project_team.rb b/app/models/project_team.rb index c797c36882fa4cd2a931c9e66befbe5eae3671bc..bc35c4041ba6f2fe14dced4fc69125c33eaaa7f4 100644 --- a/app/models/project_team.rb +++ b/app/models/project_team.rb @@ -32,7 +32,15 @@ class ProjectTeam end def find_tm(user_id) - project.users_projects.find_by_user_id(user_id) + tm = project.users_projects.find_by_user_id(user_id) + + # If user is not in project members + # we should check for group membership + if group && !tm + tm = group.users_groups.find_by_user_id(user_id) + end + + tm end def add_user(user, access) diff --git a/app/models/repository.rb b/app/models/repository.rb index 3d649519d8fbd0131b4ca54be306377f2cba996e..aeec48ee5cc82e40c249494621ae6bb1aeb9298b 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -18,6 +18,7 @@ class Repository end def commit(id = nil) + return nil unless raw_repository commit = Gitlab::Git::Commit.find(raw_repository, id) commit = Commit.new(commit) if commit commit diff --git a/app/models/service_hook.rb b/app/models/service_hook.rb index 433e8059788016a6f48e77ae0b9bc8d7ff0b2e37..4cd2b272eece51050c3392b321b1f32900bd50b3 100644 --- a/app/models/service_hook.rb +++ b/app/models/service_hook.rb @@ -5,8 +5,8 @@ # id :integer not null, primary key # url :string(255) # project_id :integer -# created_at :datetime -# updated_at :datetime +# created_at :datetime not null +# updated_at :datetime not null # type :string(255) default("ProjectHook") # service_id :integer # diff --git a/app/models/snippet.rb b/app/models/snippet.rb index 3a2fc17e59c70e9adb6d15d787a19251d60c02b7..edc179b20fd60f0dd4576827ca3a8888548bdc08 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -4,11 +4,11 @@ # # id :integer not null, primary key # title :string(255) -# content :text +# content :text(2147483647) # author_id :integer not null # project_id :integer -# created_at :datetime -# updated_at :datetime +# created_at :datetime not null +# updated_at :datetime not null # file_name :string(255) # expires_at :datetime # private :boolean default(TRUE), not null diff --git a/app/models/system_hook.rb b/app/models/system_hook.rb index 2d28f4cd45285d986e645cc348ba8567f2b3aa9e..5cdf046644f184b0536f45f3c3832c84689dc977 100644 --- a/app/models/system_hook.rb +++ b/app/models/system_hook.rb @@ -5,8 +5,8 @@ # id :integer not null, primary key # url :string(255) # project_id :integer -# created_at :datetime -# updated_at :datetime +# created_at :datetime not null +# updated_at :datetime not null # type :string(255) default("ProjectHook") # service_id :integer # diff --git a/app/models/user.rb b/app/models/user.rb index a149b3a0322785d2e0a4c67f5b5b6416ffeb233b..ea1b9419288557f6e38da6ef81262485cd8918d8 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -4,7 +4,7 @@ # # id :integer not null, primary key # email :string(255) default(""), not null -# encrypted_password :string(128) default(""), not null +# encrypted_password :string(255) default(""), not null # reset_password_token :string(255) # reset_password_sent_at :datetime # remember_created_at :datetime @@ -13,8 +13,8 @@ # last_sign_in_at :datetime # current_sign_in_ip :string(255) # last_sign_in_ip :string(255) -# created_at :datetime -# updated_at :datetime +# created_at :datetime not null +# updated_at :datetime not null # name :string(255) # admin :boolean default(FALSE), not null # projects_limit :integer default(10) @@ -47,7 +47,7 @@ class User < ActiveRecord::Base :extern_uid, :provider, :password_expires_at, as: [:default, :admin] - attr_accessible :projects_limit, :can_create_team, :can_create_group, + attr_accessible :projects_limit, :can_create_group, as: :admin attr_accessor :force_random_password @@ -126,6 +126,17 @@ class User < ActiveRecord::Base after_transition any => :blocked do |user, transition| # Remove user from all projects and user.users_projects.find_each do |membership| + # skip owned resources + next if membership.project.owner == user + + return false unless membership.destroy + end + + # Remove user from all groups + user.users_groups.find_each do |membership| + # skip owned resources + next if membership.group.owners.include?(user) + return false unless membership.destroy end end @@ -148,6 +159,7 @@ class User < ActiveRecord::Base 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) ) : scoped } scope :without_projects, -> { where('id NOT IN (SELECT DISTINCT(user_id) FROM users_projects)') } + scope :ldap, -> { where(provider: 'ldap') } scope :potential_team_members, ->(team) { team.members.any? ? active.not_in_team(team) : active } @@ -175,24 +187,31 @@ class User < ActiveRecord::Base end end - def create_from_omniauth(auth, ldap = false) - gitlab_auth.create_from_omniauth(auth, ldap) - end - - def find_or_new_for_omniauth(auth) - gitlab_auth.find_or_new_for_omniauth(auth) + def search query + where("name LIKE :query OR email LIKE :query OR username LIKE :query", query: "%#{query}%") end - def find_for_ldap_auth(auth, signed_in_resource = nil) - gitlab_auth.find_for_ldap_auth(auth, signed_in_resource) + def by_username_or_id(name_or_id) + if (name_or_id.is_a?(Integer)) + User.find_by_id(name_or_id) + else + User.find_by_username(name_or_id) + end end - def gitlab_auth - Gitlab::Auth.new + def build_user(attrs = {}, options= {}) + user = User.new(defaults.merge(attrs), options) + # if not as: :admin force default settings + user.with_defaults unless options[:as] == :admin + user end - def search query - where("name LIKE :query OR email LIKE :query OR username LIKE :query", query: "%#{query}%") + def defaults + { + projects_limit: Gitlab.config.gitlab.default_projects_limit, + can_create_group: Gitlab.config.gitlab.default_can_create_group, + theme_id: Gitlab::Theme::BASIC + } end end @@ -204,15 +223,6 @@ class User < ActiveRecord::Base username end - def with_defaults - tap do |u| - u.projects_limit = Gitlab.config.gitlab.default_projects_limit - u.can_create_group = Gitlab.config.gitlab.default_can_create_group - u.can_create_team = Gitlab.config.gitlab.default_can_create_team - u.theme_id = Gitlab::Theme::MARS - end - end - def notification @notification ||= Notification.new(self) end @@ -319,7 +329,7 @@ class User < ActiveRecord::Base end def several_namespaces? - namespaces.many? + namespaces.many? || owned_groups.any? end def namespace_id @@ -366,4 +376,16 @@ class User < ActiveRecord::Base self.send("#{attr}=", Sanitize.clean(value)) if value.present? end end + + def solo_owned_groups + @solo_owned_groups ||= owned_groups.select do |group| + group.owners == [self] + end + end + + def with_defaults + User.defaults.each do |k,v| + self.send("#{k}=",v) + end + end end diff --git a/app/models/users_group.rb b/app/models/users_group.rb index b334066b8c8539441837f58bcd1535f17fc1b8b6..181bf3222832b98d26f84938d95b335fa9c1e8bd 100644 --- a/app/models/users_group.rb +++ b/app/models/users_group.rb @@ -2,31 +2,21 @@ # # 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 not null -# updated_at :datetime not null +# id :integer not null, primary key +# group_access :integer not null +# group_id :integer not null +# user_id :integer not null +# created_at :datetime not null +# updated_at :datetime not null +# notification_level :integer default(3), not null # class UsersGroup < ActiveRecord::Base include Notifiable - - GUEST = 10 - REPORTER = 20 - DEVELOPER = 30 - MASTER = 40 - OWNER = 50 + include Gitlab::Access def self.group_access_roles - { - "Guest" => GUEST, - "Reporter" => REPORTER, - "Developer" => DEVELOPER, - "Master" => MASTER, - "Owner" => OWNER - } + Gitlab::Access.options_with_owner end attr_accessible :group_access, :user_id @@ -50,7 +40,7 @@ class UsersGroup < ActiveRecord::Base delegate :name, :username, :email, to: :user, prefix: true - def human_access - UsersGroup.group_access_roles.key(self.group_access) + def access_field + group_access end end diff --git a/app/models/users_project.rb b/app/models/users_project.rb index 45e9305cafbbc05e628b41cb2cb5aae0340dd880..6f147859a5cf538184b7dba91e7cc31944073136 100644 --- a/app/models/users_project.rb +++ b/app/models/users_project.rb @@ -5,8 +5,8 @@ # id :integer not null, primary key # user_id :integer not null # project_id :integer not null -# created_at :datetime -# updated_at :datetime +# created_at :datetime not null +# updated_at :datetime not null # project_access :integer default(0), not null # notification_level :integer default(3), not null # @@ -14,11 +14,7 @@ class UsersProject < ActiveRecord::Base include Gitlab::ShellAdapter include Notifiable - - GUEST = 10 - REPORTER = 20 - DEVELOPER = 30 - MASTER = 40 + include Gitlab::Access attr_accessible :user, :user_id, :project_access @@ -27,7 +23,7 @@ class UsersProject < ActiveRecord::Base validates :user, presence: true validates :user_id, uniqueness: { scope: [:project_id], message: "already exists in project" } - validates :project_access, inclusion: { in: [GUEST, REPORTER, DEVELOPER, MASTER] }, presence: true + validates :project_access, inclusion: { in: Gitlab::Access.values }, presence: true validates :project, presence: true delegate :name, :username, :email, to: :user, prefix: true @@ -103,27 +99,19 @@ class UsersProject < ActiveRecord::Base end def roles_hash - { - guest: GUEST, - reporter: REPORTER, - developer: DEVELOPER, - master: MASTER - } + Gitlab::Access.sym_options end def access_roles - { - "Guest" => GUEST, - "Reporter" => REPORTER, - "Developer" => DEVELOPER, - "Master" => MASTER - } + Gitlab::Access.options end end - def project_access_human - Project.access_options.key(self.project_access) + def access_field + project_access end - alias_method :human_access, :project_access_human + def owner? + project.owner == user + end end diff --git a/app/models/web_hook.rb b/app/models/web_hook.rb index 1e6ea885ea9dd7ed8e7c07057992e9e60f60f646..3f22b1082fb0aaad525f8b57be062622b5501240 100644 --- a/app/models/web_hook.rb +++ b/app/models/web_hook.rb @@ -5,8 +5,8 @@ # id :integer not null, primary key # url :string(255) # project_id :integer -# created_at :datetime -# updated_at :datetime +# created_at :datetime not null +# updated_at :datetime not null # type :string(255) default("ProjectHook") # service_id :integer # diff --git a/app/observers/activity_observer.rb b/app/observers/activity_observer.rb index 477ebd78aebf6b3e0790c64603ba4c851729262c..d754ac8185a50c011bd2c4a99b97392994850086 100644 --- a/app/observers/activity_observer.rb +++ b/app/observers/activity_observer.rb @@ -5,8 +5,8 @@ class ActivityObserver < BaseObserver event_author_id = record.author_id if record.kind_of?(Note) - # Skip system status notes like 'status changed to close' - return true if record.note.include?("_Status changed to ") + # Skip system notes, like status changes and cross-references. + return true if record.system? # Skip wall notes to prevent spamming of dashboard return true if record.noteable_type.blank? diff --git a/app/observers/base_observer.rb b/app/observers/base_observer.rb index 92b73981d27cc89cda9dae0fdd741ddfa67397da..f9a0242ce770f2479fed5c8c8de4943720010e9f 100644 --- a/app/observers/base_observer.rb +++ b/app/observers/base_observer.rb @@ -10,4 +10,8 @@ class BaseObserver < ActiveRecord::Observer def current_user Thread.current[:current_user] end + + def current_commit + Thread.current[:current_commit] + end end diff --git a/app/observers/issue_observer.rb b/app/observers/issue_observer.rb index 50538419776a6b170d10ba624934b7429e51fc47..886d8b776fb7612df313835417f9e65cbddab366 100644 --- a/app/observers/issue_observer.rb +++ b/app/observers/issue_observer.rb @@ -1,6 +1,8 @@ class IssueObserver < BaseObserver def after_create(issue) notification.new_issue(issue, current_user) + + issue.create_cross_references!(issue.project, current_user) end def after_close(issue, transition) @@ -17,12 +19,14 @@ class IssueObserver < BaseObserver if issue.is_being_reassigned? notification.reassigned_issue(issue, current_user) end + + issue.notice_added_references(issue.project, current_user) end protected # Create issue note with service comment like 'Status changed to closed' def create_note(issue) - Note.create_status_change_note(issue, issue.project, current_user, issue.state) + Note.create_status_change_note(issue, issue.project, current_user, issue.state, current_commit) end end diff --git a/app/observers/merge_request_observer.rb b/app/observers/merge_request_observer.rb index 689a25ff4b79c80ad3a376167bbdaef52d7660dc..d70da514cd20d47195a027603e2655df9b0e2936 100644 --- a/app/observers/merge_request_observer.rb +++ b/app/observers/merge_request_observer.rb @@ -7,11 +7,13 @@ class MergeRequestObserver < ActivityObserver end notification.new_merge_request(merge_request, current_user) + + merge_request.create_cross_references!(merge_request.project, current_user) end def after_close(merge_request, transition) create_event(merge_request, Event::CLOSED) - Note.create_status_change_note(merge_request, merge_request.target_project, current_user, merge_request.state) + Note.create_status_change_note(merge_request, merge_request.target_project, current_user, merge_request.state, nil) notification.close_mr(merge_request, current_user) end @@ -33,11 +35,13 @@ class MergeRequestObserver < ActivityObserver def after_reopen(merge_request, transition) create_event(merge_request, Event::REOPENED) - Note.create_status_change_note(merge_request, merge_request.target_project, current_user, merge_request.state) + Note.create_status_change_note(merge_request, merge_request.target_project, current_user, merge_request.state, nil) end def after_update(merge_request) notification.reassigned_merge_request(merge_request, current_user) if merge_request.is_being_reassigned? + + merge_request.notice_added_references(merge_request.project, current_user) end def create_event(record, status) diff --git a/app/observers/note_observer.rb b/app/observers/note_observer.rb index 7b79161cce4b9573003976a5e85afaa4429640af..d31b6e8d7ce6c4a363434004ae80c28f2229032c 100644 --- a/app/observers/note_observer.rb +++ b/app/observers/note_observer.rb @@ -1,5 +1,17 @@ class NoteObserver < BaseObserver def after_create(note) notification.new_note(note) + + unless note.system? + # Create a cross-reference note if this Note contains GFM that names an + # issue, merge request, or commit. + note.references.each do |mentioned| + Note.create_cross_reference_note(mentioned, note.noteable, note.author, note.project) + end + end + end + + def after_update(note) + note.notice_added_references(note.project, current_user) end end diff --git a/app/observers/user_observer.rb b/app/observers/user_observer.rb index e969405a5985c63ae77a6a9b4c76661c30c53a74..fba0f1006d924ffa5ddf7757f2e2021ff6f65b0e 100644 --- a/app/observers/user_observer.rb +++ b/app/observers/user_observer.rb @@ -13,8 +13,8 @@ class UserObserver < BaseObserver # Ensure user has namespace user.create_namespace!(path: user.username, name: user.username) unless user.namespace - if user.username_changed? || user.name_changed? - user.namespace.update_attributes(path: user.username, name: user.name) + if user.username_changed? + user.namespace.update_attributes(path: user.username, name: user.username) end end end diff --git a/app/observers/users_group_observer.rb b/app/observers/users_group_observer.rb new file mode 100644 index 0000000000000000000000000000000000000000..ecdbede89d96b1f29e5f1839660f552886b0cc28 --- /dev/null +++ b/app/observers/users_group_observer.rb @@ -0,0 +1,9 @@ +class UsersGroupObserver < BaseObserver + def after_create(membership) + notification.new_group_member(membership) + end + + def after_update(membership) + notification.update_group_member(membership) + end +end diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index e8b32f52ce155d39673c9f0501b4cea45a09a32d..f9d43e60de6729b114f6b910a1febe0572a16d5c 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -1,22 +1,24 @@ class GitPushService - attr_accessor :project, :user, :push_data + attr_accessor :project, :user, :push_data, :push_commits # This method will be called after each git update # and only if the provided user and project is present in GitLab. # # All callbacks for post receive action should be placed here. # - # Now this method do next: - # 1. Ensure project satellite exists - # 2. Update merge requests - # 3. Execute project web hooks - # 4. Execute project services - # 5. Create Push Event + # Next, this method: + # 1. Creates the push event + # 2. Ensures that the project satellite exists + # 3. Updates merge requests + # 4. Recognizes cross-references from commit messages + # 5. Executes the project's web hooks + # 6. Executes the project's services # def execute(project, user, oldrev, newrev, ref) @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 @@ -25,11 +27,30 @@ class GitPushService project.discover_default_branch project.repository.expire_cache - if push_to_branch?(ref, oldrev) + 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) project.execute_hooks(@push_data.dup) project.execute_services(@push_data.dup) end + + if push_to_new_branch?(ref, oldrev) + # Re-find the pushed commits. + if is_default_branch?(ref) + # Initial push to the default branch. Take the full history of that branch as "newly pushed". + @push_commits = project.repository.commits(newrev) + else + # 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 + # that shouldn't matter because we check for existing cross-references later. + @push_commits = project.repository.commits_between(project.default_branch, newrev) + end + + process_commit_messages(ref) + end end # This method provide a sample data @@ -38,14 +59,14 @@ class GitPushService # def sample_data(project, user) @project, @user = project, user - commits = project.repository.commits(project.default_branch, nil, 3) - post_receive_data(commits.last.id, commits.first.id, "refs/heads/#{project.default_branch}") + @push_commits = project.repository.commits(project.default_branch, nil, 3) + post_receive_data(@push_commits.last.id, @push_commits.first.id, "refs/heads/#{project.default_branch}") end protected def create_push_event - Event.create( + Event.create!( project: project, action: Event::PUSHED, data: push_data, @@ -53,6 +74,36 @@ class GitPushService ) end + # Extract any GFM references from the pushed commit messages. If the configured issue-closing regex is matched, + # close the referenced Issue. Create cross-reference Notes corresponding to any other referenced Mentionables. + def process_commit_messages ref + is_default_branch = is_default_branch?(ref) + + @push_commits.each do |commit| + # Close issues if these commits were pushed to the project's default branch and the commit message matches the + # closing regex. Exclude any mentioned Issues from cross-referencing even if the commits are being pushed to + # a different branch. + issues_to_close = commit.closes_issues(project) + author = commit_user(commit) + + if !issues_to_close.empty? && is_default_branch + Thread.current[:current_user] = author + Thread.current[:current_commit] = commit + + issues_to_close.each { |i| i.close && i.save } + end + + # Create cross-reference notes for any other references. Omit any issues that were referenced in an + # issue-closing phrase, or have already been mentioned from this commit (probably from this commit + # being pushed to a different branch). + refs = commit.references(project) - issues_to_close + refs.reject! { |r| commit.has_mentioned?(r) } + refs.each do |r| + Note.create_cross_reference_note(r, commit, author, project) + end + end + end + # Produce a hash of post-receive data # # data = { @@ -72,8 +123,6 @@ class GitPushService # } # def post_receive_data(oldrev, newrev, ref) - push_commits = project.repository.commits_between(oldrev, newrev) - # Total commits count push_commits_count = push_commits.size @@ -116,10 +165,30 @@ class GitPushService data end - def push_to_branch? ref, oldrev + def push_to_existing_branch? ref, oldrev ref_parts = ref.split('/') # Return if this is not a push to a branch (e.g. new commits) - !(ref_parts[1] !~ /heads/ || oldrev == "00000000000000000000000000000000") + ref_parts[1] =~ /heads/ && oldrev != "0000000000000000000000000000000000000000" + end + + def push_to_new_branch? ref, oldrev + ref_parts = ref.split('/') + + ref_parts[1] =~ /heads/ && oldrev == "0000000000000000000000000000000000000000" + end + + def push_to_branch? ref + ref =~ /refs\/heads/ + end + + def is_default_branch? ref + ref == "refs/heads/#{project.default_branch}" + end + + def commit_user commit + User.where(email: commit.author_email).first || + User.where(name: commit.author_name).first || + user end end diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index c20b07e69f076293215bd9db40badf22edd2f11c..71c25dc1b70b8537eaf2331a3fefe812d489e084 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -148,6 +148,14 @@ class NotificationService mailer.project_access_granted_email(users_project.id) end + def new_group_member(users_group) + mailer.group_access_granted_email(users_group.id) + end + + def update_group_member(users_group) + mailer.group_access_granted_email(users_group.id) + end + protected # Get project users with WATCH notification level diff --git a/app/services/project_transfer_service.rb b/app/services/project_transfer_service.rb index 3b8c4847f203d7a82b9e29556ae13420478b475b..7150c1c78c0c213b8a27ebc248f125dd032fa1f4 100644 --- a/app/services/project_transfer_service.rb +++ b/app/services/project_transfer_service.rb @@ -29,6 +29,12 @@ class ProjectTransferService # Move wiki repo also if present gitlab_shell.mv_repository("#{old_path}.wiki", "#{new_path}.wiki") + # create satellite repo + project.ensure_satellite_exists + + # clear project cached events + project.reset_events_cache + true end end diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb index 01ddf99e97ba733206335f03d3285f0887c62bbf..c872b27ba393540b9601b45717c555c4900182a1 100644 --- a/app/services/system_hooks_service.rb +++ b/app/services/system_hooks_service.rb @@ -43,7 +43,7 @@ class SystemHooksService project_id: model.project_id, user_name: model.user.name, user_email: model.user.email, - project_access: model.project_access_human + project_access: model.human_access }) end end diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml index 70f0c193c01f74a33bf7155e8521f6a25cb15c75..4544a9aa190b14a9599b023e266aa0639b08e79e 100644 --- a/app/views/admin/projects/show.html.haml +++ b/app/views/admin/projects/show.html.haml @@ -12,7 +12,8 @@ %ul.well-list %li %span.light Name: - %strong= @project.name + %strong + = link_to @project.name, project_path(@project) %li %span.light Namespace: %strong @@ -77,8 +78,8 @@ - if @group .ui-box .title - %strong #{@group.name} Group - members (#{@group.users_groups.count}) + %strong #{@group.name} + group members (#{@group.users_groups.count}) .pull-right = link_to admin_group_path(@group), class: 'btn btn-small' do %i.icon-edit @@ -88,7 +89,7 @@ .ui-box .title - Team + Project members %small (#{@project.users.count}) .pull-right @@ -96,11 +97,15 @@ %i.icon-edit Manage Access %ul.well-list.team_members - - @project.users.each do |user| + - @project.users_projects.each do |users_project| + - user = users_project.user %li %strong = link_to user.name, admin_user_path(user) .pull-right - %span.light= @project.project_access_human(user) - = link_to admin_project_member_path(@project, user), confirm: remove_from_project_team_message(@project, user), method: :delete, class: "btn btn-small btn-remove" do - %i.icon-remove + - if users_project.owner? + %span.light Owner + - else + %span.light= users_project.human_access + = link_to admin_project_member_path(@project, user), confirm: remove_from_project_team_message(@project, user), method: :delete, class: "btn btn-small btn-remove" do + %i.icon-remove diff --git a/app/views/admin/users/_form.html.haml b/app/views/admin/users/_form.html.haml index 1365404edad627131fabb75fde1715e637e3c48a..3f930c45fa61647a7db27d9e3fd718cf9cf3054b 100644 --- a/app/views/admin/users/_form.html.haml +++ b/app/views/admin/users/_form.html.haml @@ -56,10 +56,6 @@ = f.label :can_create_group .controls= f.check_box :can_create_group - .control-group - = f.label :can_create_team - .controls= f.check_box :can_create_team - .control-group = f.label :admin do %strong.cred Administrator diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml index d48660f0a9e34267f05107f89cb89cb2a252b352..95257a4594ee1830599f180d7d2684f2e063b62f 100644 --- a/app/views/admin/users/show.html.haml +++ b/app/views/admin/users/show.html.haml @@ -7,15 +7,11 @@ %span.cred (Admin) .pull-right - = link_to edit_admin_user_path(@user), class: "btn grouped btn-small" do + = link_to edit_admin_user_path(@user), class: "btn grouped" do %i.icon-edit Edit - - unless @user == current_user - - if @user.blocked? - = link_to 'Unblock', unblock_admin_user_path(@user), method: :put, class: "btn grouped btn-small success" - - else - = link_to 'Block', block_admin_user_path(@user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn grouped btn-small btn-remove" - = link_to 'Destroy', [:admin, @user], confirm: "USER #{@user.name} WILL BE REMOVED! Are you sure?", method: :delete, class: "btn grouped btn-small btn-remove" + - if @user.blocked? + = link_to 'Unblock', unblock_admin_user_path(@user), method: :put, class: "btn grouped success" %hr .row @@ -63,24 +59,56 @@ %strong = link_to @user.created_by.name, [:admin, @user.created_by] + - unless @user == current_user + .alert + %h4 Block user + %br + %p Blocking user has the following effects: + %ul + %li User will not be able to login + %li User will not be able to access git repositories + %li User will be removed from joined projects and groups + %li Personal projects will be left + %li Owned groups will be left + = link_to 'Block user', block_admin_user_path(@user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn btn-remove" + + .alert.alert-error + %h4 + Remove user + %br + %p Deleting a user has the following effects: + %ul + %li All user content like authored issues, snippets, comments will be removed + - rp = @user.personal_projects.count + - unless rp.zero? + %li #{pluralize rp, 'personal project'} will be removed and cannot be restored + - if @user.solo_owned_groups.present? + %li + Next groups with all content will be removed: + %strong #{@user.solo_owned_groups.map(&:name).join(', ')} + = link_to 'Remove user', [:admin, @user], confirm: "USER #{@user.name} WILL BE REMOVED! Are you sure?", method: :delete, class: "btn btn-remove" + + .span6 - if @user.users_groups.present? .ui-box .title Groups: %ul.well-list - @user.users_groups.each do |user_group| - group = user_group.group - %li + %li.users_group %strong= link_to group.name, admin_group_path(group) .pull-right %span.light= user_group.human_access + - unless user_group.owner? + = link_to group_users_group_path(group, user_group), 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-remove.icon-white - .span6 .ui-box .title Projects (#{@projects.count}) %ul.well-list - @projects.sort_by(&:name_with_namespace).each do |project| - tm = project.team.find_tm(@user.id) - %li + %li.users_project = link_to admin_project_path(project), class: dom_class(project) do - if project.namespace = project.namespace.human_name @@ -90,6 +118,13 @@ - if tm .pull-right - %span.light= tm.human_access - = link_to admin_project_member_path(project, tm.user), confirm: remove_from_project_team_message(project, @user), method: :delete, class: "btn btn-small btn-remove" do - %i.icon-remove + - if tm.owner? + %span.light Owner + - else + %span.light= tm.human_access + + - if tm.respond_to? :project + = link_to project_team_member_path(project, @user), confirm: remove_from_project_team_message(project, @user), remote: true, method: :delete, class: "btn-tiny btn btn-remove", title: 'Remove user from project' do + %i.icon-remove + + diff --git a/app/views/dashboard/_activities.html.haml b/app/views/dashboard/_activities.html.haml index 2b7d23c225d49f770b6d150661e74f319c75638f..891177263173f01ac8e38a598084d0998c8714df 100644 --- a/app/views/dashboard/_activities.html.haml +++ b/app/views/dashboard/_activities.html.haml @@ -1,10 +1,5 @@ = render "events/event_last_push", event: @last_push - -.event_filter - = event_filter_link EventFilter.push, 'Push events' - = event_filter_link EventFilter.merged, 'Merge events' - = event_filter_link EventFilter.comments, 'Comments' - = event_filter_link EventFilter.team, 'Team' += render 'shared/event_filter' - if @events.any? .content_list diff --git a/app/views/dashboard/_groups.html.haml b/app/views/dashboard/_groups.html.haml index 9aca7b353f3d16b9d6bdbabdb71ba9462e51157f..8b32c5642bb95b485981ee80592484de6095fbd9 100644 --- a/app/views/dashboard/_groups.html.haml +++ b/app/views/dashboard/_groups.html.haml @@ -3,9 +3,9 @@ = search_field_tag :filter_group, nil, placeholder: 'Filter by name', class: 'dash-filter' - if current_user.can_create_group? %span.pull-right - = link_to new_group_path, class: "btn" do + = link_to new_group_path, class: "btn btn-new" do %i.icon-plus - New Group + New group %ul.well-list.dash-list - groups.each do |group| %li.group-row diff --git a/app/views/dashboard/_projects.html.haml b/app/views/dashboard/_projects.html.haml index 305b83771a106dd9aca6178dc5e99aaf9b44d432..90b843de016d665e469a6138046dd46092ae69f3 100644 --- a/app/views/dashboard/_projects.html.haml +++ b/app/views/dashboard/_projects.html.haml @@ -3,9 +3,9 @@ = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'dash-filter' - if current_user.can_create_project? %span.pull-right - = link_to new_project_path, class: "btn" do + = link_to new_project_path, class: "btn btn-new" do %i.icon-plus - New Project + New project %ul.well-list.dash-list - projects.each do |project| diff --git a/app/views/dashboard/_zero_authorized_projects.html.haml b/app/views/dashboard/_zero_authorized_projects.html.haml index ea5d8d6376fa9fb71df11f2bc37b2e9bd768a577..79d5dca8845d74dce9ce7732847d8d8b4511cc4c 100644 --- a/app/views/dashboard/_zero_authorized_projects.html.haml +++ b/app/views/dashboard/_zero_authorized_projects.html.haml @@ -1,12 +1,36 @@ -%h3.nothing_here_message - You don't have access to any projects. - %br - - if current_user.can_create_project? - You can create up to - = pluralize(current_user.projects_limit, "project") + "." - Click on the button below to add a new one - .link_holder - = link_to new_project_path, class: "btn btn-primary" do - New Project » - - else - If you are added to a project, it will be displayed here +%h3.page-title Welcome to GitLab! +%p.light Self Hosted Git Management application. +%hr +%div + .dashboard-intro-icon + %i.icon-bookmark-empty + %div + %p.slead + You don't have access to any projects right now. + %br + - if current_user.can_create_project? + You can create up to + %strong= pluralize(current_user.projects_limit, "project") + "." + Click on the button below to add a new one + - else + If you are added to a project, it will be displayed here + + - if current_user.can_create_project? + .link_holder + = link_to new_project_path, class: "btn btn-new" do + New project » + +- if current_user.can_create_group? + %hr + %div + .dashboard-intro-icon + %i.icon-group + %div + %p.slead + You can create a group for several dependent projects. + %br + Group is the best way to manage projects and members + .link_holder + = link_to new_group_path, class: "btn btn-new" do + New group » + diff --git a/app/views/dashboard/projects.html.haml b/app/views/dashboard/projects.html.haml index 3599320dfb36ad82e965fe77784e766472d341ee..0dcb1a87e9aaadc3a12b66302136b824c15f41f0 100644 --- a/app/views/dashboard/projects.html.haml +++ b/app/views/dashboard/projects.html.haml @@ -26,14 +26,30 @@ %span.pull-right = current_user.owned_projects.count + + - if @groups.present? + %fieldset + %legend Groups + %ul.bordered-list + - @groups.each do |group| + %li{ class: (group.name == params[:group]) ? 'active' : 'light' } + = link_to projects_dashboard_path(group: group.name) do + %i.icon-folder-close-alt + = group.name + %small.pull-right + = group.projects.count + + + - if @labels.present? - %p.light Filter by label: - %ul.bordered-list - - @labels.each do |label| - %li{ class: (label.name == params[:label]) ? 'active' : 'light' } - = link_to projects_dashboard_path(scope: params[:scope], label: label.name) do - %i.icon-tag - = label.name + %fieldset + %legend Labels + %ul.bordered-list + - @labels.each do |label| + %li{ class: (label.name == params[:label]) ? 'active' : 'light' } + = link_to projects_dashboard_path(scope: params[:scope], label: label.name) do + %i.icon-tag + = label.name .span9 %ul.bordered-list.my-projects.top-list diff --git a/app/views/events/_commit.html.haml b/app/views/events/_commit.html.haml index b50faf5a25cbe3dd46326e3d5904d8263bf90842..4d4b24009f43cd9923cb0b878eebe1d7cfb14b4a 100644 --- a/app/views/events/_commit.html.haml +++ b/app/views/events/_commit.html.haml @@ -1,7 +1,5 @@ %li.commit - %p + .commit-row-title = link_to commit[:id][0..8], project_commit_path(project, commit[:id]), class: "commit_short_id", alt: '' - %span= commit[:author][:name] - – - = image_tag gravatar_icon(commit[:author][:email]), class: "avatar", width: 16 - = gfm escape_once(truncate(commit[:message], length: 50)) rescue "--broken encoding" + + = gfm escape_once(truncate(commit[:message], length: 70)) rescue "--broken encoding" diff --git a/app/views/events/event/_common.html.haml b/app/views/events/event/_common.html.haml index 6989f862f473beaa60e28790b3b2ca9294318c40..a9d3adf41dfb3e070a3e0feb7fc08ceab9f59c61 100644 --- a/app/views/events/event/_common.html.haml +++ b/app/views/events/event/_common.html.haml @@ -2,7 +2,7 @@ %span.author_name= link_to_author event %span.event_label{class: event.action_name}= event_action_name(event) - if event.target - %strong= link_to "##{event.target_id}", [event.project, event.target] + %strong= link_to "##{event.target_iid}", [event.project, event.target] - else %strong= gfm event.target_title at diff --git a/app/views/events/event/_push.html.haml b/app/views/events/event/_push.html.haml index f2b8dc4238c6462dd18df32bda9ff9fea8b06829..adba9a5f61975994590eb9ac704d9e3cd5c520a6 100644 --- a/app/views/events/event/_push.html.haml +++ b/app/views/events/event/_push.html.haml @@ -5,7 +5,7 @@ %strong= event.ref_name - else = link_to project_commits_path(event.project, event.ref_name) do - %strong= event.ref_name + %strong= truncate(event.ref_name, length: 30) at %strong= link_to_project event.project diff --git a/app/views/groups/_projects.html.haml b/app/views/groups/_projects.html.haml index cbad2e3e522495e1ede3ed08cecf3024b6ca80c2..16a3c60f660a451377765e6a41e67c616652b427 100644 --- a/app/views/groups/_projects.html.haml +++ b/app/views/groups/_projects.html.haml @@ -3,9 +3,9 @@ Projects (#{projects.count}) - if can? current_user, :manage_group, @group %span.pull-right - = link_to new_project_path(namespace_id: @group.id), class: "btn" do + = link_to new_project_path(namespace_id: @group.id), class: "btn btn-new" do %i.icon-plus - New Project + New project %ul.well-list - if projects.blank? %p.nothing_here_message This groups has no projects yet diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml index fe66f89626fe658d89a54db4d1c7129e274ca102..8aefdf7f5132922a06c4aff2f090cc3a2a306d34 100644 --- a/app/views/groups/edit.html.haml +++ b/app/views/groups/edit.html.haml @@ -59,7 +59,7 @@ = private_icon = link_to project.name_with_namespace, project .pull-right - = link_to 'Team', project_team_index_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small" + = link_to 'Members', project_team_index_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small" = link_to 'Edit', edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small" = link_to 'Remove', project, confirm: remove_project_message(project), method: :delete, class: "btn btn-small btn-remove" - if @group.projects.blank? diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index 8afc4ab4a12430f120f738cee4829a22b3a1469f..e613ed3eaa3531b7bc70fa05d8f76da30a8e8d80 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -6,6 +6,7 @@ %span.cgray You will only see events from projects in this group %hr + = render 'shared/event_filter' - if @events.any? .content_list - else diff --git a/app/views/layouts/_head_panel.html.haml b/app/views/layouts/_head_panel.html.haml index 5644c89016bd584f8baa0f972f4fb13cad2219fb..6492c122ba04c97505a426b719a5bf0ec357d349 100644 --- a/app/views/layouts/_head_panel.html.haml +++ b/app/views/layouts/_head_panel.html.haml @@ -27,10 +27,10 @@ %i.icon-cogs - if current_user.can_create_project? %li - = link_to new_project_path, title: "Create New Project", class: 'has_bottom_tooltip', 'data-original-title' => 'New project' do + = link_to new_project_path, title: "New project", class: 'has_bottom_tooltip', 'data-original-title' => 'New project' do %i.icon-plus %li - = link_to profile_path, title: "My Profile", class: 'has_bottom_tooltip', 'data-original-title' => 'Your profile' do + = link_to profile_path, title: "My profile", class: 'has_bottom_tooltip', 'data-original-title' => 'My profile' do %i.icon-user %li = link_to destroy_user_session_path, class: "logout", method: :delete, title: "Logout", class: 'has_bottom_tooltip', 'data-original-title' => 'Logout' do diff --git a/app/views/notify/closed_issue_email.html.haml b/app/views/notify/closed_issue_email.html.haml index 23ccc4538dea8773e1f82c9b4952dacb43234c39..325cd44eb4b0807e626a9dfa3be538adc167e2fb 100644 --- a/app/views/notify/closed_issue_email.html.haml +++ b/app/views/notify/closed_issue_email.html.haml @@ -1,5 +1,5 @@ %p = "Issue was closed by #{@updated_by.name}" %p - = "Issue ##{@issue.id}" + = "Issue ##{@issue.iid}" = link_to_gfm truncate(@issue.title, length: 45), project_issue_url(@issue.project, @issue), title: @issue.title diff --git a/app/views/notify/closed_issue_email.text.haml b/app/views/notify/closed_issue_email.text.haml index 0cca321552c1530e13f10d04aa8a56c708923602..49f160a0d5fbf397b237e36a036d05fa9412bf56 100644 --- a/app/views/notify/closed_issue_email.text.haml +++ b/app/views/notify/closed_issue_email.text.haml @@ -1,3 +1,3 @@ = "Issue was closed by #{@updated_by.name}" -Issue ##{@issue.id}: #{project_issue_url(@issue.project, @issue)} +Issue ##{@issue.iid}: #{project_issue_url(@issue.project, @issue)} diff --git a/app/views/notify/closed_merge_request_email.html.haml b/app/views/notify/closed_merge_request_email.html.haml index c6b45b1710a65dfdc81a1c51c6a5887dc35c2057..45770cc85ded25554b503f75ea64c822c3cba2ff 100644 --- a/app/views/notify/closed_merge_request_email.html.haml +++ b/app/views/notify/closed_merge_request_email.html.haml @@ -1,5 +1,5 @@ %p - = "Merge Request #{@merge_request.id} was closed by #{@updated_by.name}" + = "Merge Request #{@merge_request.iid} was closed by #{@updated_by.name}" %p = link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.target_project, @merge_request) %p diff --git a/app/views/notify/closed_merge_request_email.text.haml b/app/views/notify/closed_merge_request_email.text.haml index 9a5bce5915afdf7411c09cc2d6291fd0ee012ef2..ee434ec8cb21cec7cebd6ab4d00e21edc947daee 100644 --- a/app/views/notify/closed_merge_request_email.text.haml +++ b/app/views/notify/closed_merge_request_email.text.haml @@ -1,4 +1,4 @@ -= "Merge Request #{@merge_request.id} was closed by #{@updated_by.name}" += "Merge Request #{@merge_request.iid} was closed by #{@updated_by.name}" Merge Request url: #{project_merge_request_url(@merge_request.target_project, @merge_request)} diff --git a/app/views/notify/group_access_granted_email.html.haml b/app/views/notify/group_access_granted_email.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..5023ec737a50b947528a03284b8d25a28c24e79f --- /dev/null +++ b/app/views/notify/group_access_granted_email.html.haml @@ -0,0 +1,5 @@ +%p + = "You have been granted #{@membership.human_access} access to group" +%p + = link_to group_url(@group) do + = @group.name diff --git a/app/views/notify/group_access_granted_email.text.erb b/app/views/notify/group_access_granted_email.text.erb new file mode 100644 index 0000000000000000000000000000000000000000..331bb98d5c94ab22439e93c8617a00b59f5cdc7f --- /dev/null +++ b/app/views/notify/group_access_granted_email.text.erb @@ -0,0 +1,4 @@ + +You have been granted <%= @membership.human_access %> access to group <%= @group.name %> + +<%= url_for(group_url(@group)) %> diff --git a/app/views/notify/issue_status_changed_email.html.haml b/app/views/notify/issue_status_changed_email.html.haml index 4cdb70994ce46fca3be76e0f13a4faf6588925c3..7706b3f7516259fb74b434c08bb12bcb76e7a553 100644 --- a/app/views/notify/issue_status_changed_email.html.haml +++ b/app/views/notify/issue_status_changed_email.html.haml @@ -1,5 +1,5 @@ %p = "Issue was #{@issue_status} by #{@updated_by.name}" %p - = "Issue ##{@issue.id}" + = "Issue ##{@issue.iid}" = link_to_gfm truncate(@issue.title, length: 45), project_issue_url(@issue.project, @issue), title: @issue.title diff --git a/app/views/notify/issue_status_changed_email.text.erb b/app/views/notify/issue_status_changed_email.text.erb index bbca3474d5053f4471c7ea88b850ece9f3df8b08..4200881f7e8a009086be96aac46dcc8b8b41accd 100644 --- a/app/views/notify/issue_status_changed_email.text.erb +++ b/app/views/notify/issue_status_changed_email.text.erb @@ -1,4 +1,4 @@ Issue was <%= @issue_status %> by <%= @updated_by.name %> -Issue <%= @issue.id %>: <%= url_for(project_issue_url(@issue.project, @issue)) %> +Issue <%= @issue.iid %>: <%= url_for(project_issue_url(@issue.project, @issue)) %> diff --git a/app/views/notify/merged_merge_request_email.html.haml b/app/views/notify/merged_merge_request_email.html.haml index e3ae41819f872f488b62d6f2c39a8a9642d4c4bd..e2bc9cf5c0442fdbeeadc7fc58fd2a31ab3b0d34 100644 --- a/app/views/notify/merged_merge_request_email.html.haml +++ b/app/views/notify/merged_merge_request_email.html.haml @@ -1,5 +1,5 @@ %p - = "Merge Request #{@merge_request.id} was merged" + = "Merge Request #{@merge_request.iid} was merged" %p = link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.target_project, @merge_request) %p diff --git a/app/views/notify/merged_merge_request_email.text.haml b/app/views/notify/merged_merge_request_email.text.haml index fa9ef0a4b3216fe339dc4b9755da70b89aae0cbb..550f677fed49c8f3717e7aad6de900217b1205e3 100644 --- a/app/views/notify/merged_merge_request_email.text.haml +++ b/app/views/notify/merged_merge_request_email.text.haml @@ -1,4 +1,4 @@ -= "Merge Request #{@merge_request.id} was merged" += "Merge Request #{@merge_request.iid} was merged" Merge Request Url: #{project_merge_request_url(@merge_request.target_project, @merge_request)} diff --git a/app/views/notify/new_issue_email.html.haml b/app/views/notify/new_issue_email.html.haml index f11c850641bf3ff3ecc247eb88ed8025112c3a37..3b3c148517a485ee5d696d55560ff4c3c97b87ce 100644 --- a/app/views/notify/new_issue_email.html.haml +++ b/app/views/notify/new_issue_email.html.haml @@ -1,7 +1,7 @@ %p New Issue was created. %p - = "Issue ##{@issue.id}" + = "Issue ##{@issue.iid}" = link_to_gfm truncate(@issue.title, length: 45), project_issue_url(@issue.project, @issue), title: @issue.title %p Author: #{@issue.author_name} diff --git a/app/views/notify/new_issue_email.text.erb b/app/views/notify/new_issue_email.text.erb index 9907ca83247227543b9e744cc48c46f5b4d6adae..d36f54eb1ca582d6fd271bf666f56ce2df187976 100644 --- a/app/views/notify/new_issue_email.text.erb +++ b/app/views/notify/new_issue_email.text.erb @@ -1,5 +1,5 @@ New Issue was created. - -Issue <%= @issue.id %>: <%= url_for(project_issue_url(@issue.project, @issue)) %> + +Issue <%= @issue.iid %>: <%= url_for(project_issue_url(@issue.project, @issue)) %> Author: <%= @issue.author_name %> Asignee: <%= @issue.assignee_name %> diff --git a/app/views/notify/new_merge_request_email.html.haml b/app/views/notify/new_merge_request_email.html.haml index 3b060001e6bada597d6bc70687f0732b25f45b6b..6b2782ebb0b9b7a03477336c607632837fe68c1d 100644 --- a/app/views/notify/new_merge_request_email.html.haml +++ b/app/views/notify/new_merge_request_email.html.haml @@ -1,5 +1,5 @@ %p - = "New Merge Request !#{@merge_request.id}" + = "New Merge Request !#{@merge_request.iid}" %p = link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.target_project, @merge_request) %p diff --git a/app/views/notify/new_merge_request_email.text.erb b/app/views/notify/new_merge_request_email.text.erb index 3f711840a516617f9412021355f3856edcbefe9f..2d27350486e4f0e36a1393ab890ec50157ff300e 100644 --- a/app/views/notify/new_merge_request_email.text.erb +++ b/app/views/notify/new_merge_request_email.text.erb @@ -1,4 +1,4 @@ -New Merge Request <%= @merge_request.id %> +New Merge Request <%= @merge_request.iid %> <%= url_for(project_merge_request_url(@merge_request.target_project, @merge_request)) %> diff --git a/app/views/notify/new_user_email.text.erb b/app/views/notify/new_user_email.text.erb index 50ed595b38ac3d8a871bc3eb5fc40469aba9589e..c21c95d30474ec3dc20d39a2f22142fae02772d7 100644 --- a/app/views/notify/new_user_email.text.erb +++ b/app/views/notify/new_user_email.text.erb @@ -5,7 +5,7 @@ The Administrator created an account for you. Now you are a member of the compan login.................. <%= @user.email %> <% if @user.created_by_id %> password............... <%= @password %> - + You will be forced to change this password immediately after login. <% end %> diff --git a/app/views/notify/note_issue_email.html.haml b/app/views/notify/note_issue_email.html.haml index ca1331260d358d4106bc7d69f2711f1af3775dae..b3230953e7ded9183ec393156b426b658930ea6d 100644 --- a/app/views/notify/note_issue_email.html.haml +++ b/app/views/notify/note_issue_email.html.haml @@ -1,4 +1,4 @@ %p - = "New comment for Issue ##{@issue.id}" + = "New comment for Issue ##{@issue.iid}" = link_to_gfm truncate(@issue.title, length: 35), project_issue_url(@issue.project, @issue, anchor: "note_#{@note.id}") = render 'note_message' diff --git a/app/views/notify/note_issue_email.text.erb b/app/views/notify/note_issue_email.text.erb index a476b286ae457707115410affac11a356dcbd412..8a61f54a33744880c46f9ff36bbb57df1bd7df40 100644 --- a/app/views/notify/note_issue_email.text.erb +++ b/app/views/notify/note_issue_email.text.erb @@ -1,4 +1,4 @@ -New comment for Issue <%= @issue.id %> +New comment for Issue <%= @issue.iid %> <%= url_for(project_issue_url(@issue.project, @issue, anchor: "note_#{@note.id}")) %> diff --git a/app/views/notify/note_merge_request_email.html.haml b/app/views/notify/note_merge_request_email.html.haml index 14abcca607014b4b140b87e1d4a173a4a5409f92..d587b0684863d5adfba8e27cb7bee5f7eeab6a48 100644 --- a/app/views/notify/note_merge_request_email.html.haml +++ b/app/views/notify/note_merge_request_email.html.haml @@ -3,6 +3,6 @@ = link_to "New comment on diff", diffs_project_merge_request_url(@merge_request.target_project, @merge_request, anchor: "note_#{@note.id}") - else = link_to "New comment", project_merge_request_url(@merge_request.target_project, @merge_request, anchor: "note_#{@note.id}") - for Merge Request ##{@merge_request.id} + for Merge Request ##{@merge_request.iid} %cite "#{truncate(@merge_request.title, length: 20)}" = render 'note_message' diff --git a/app/views/notify/note_merge_request_email.text.erb b/app/views/notify/note_merge_request_email.text.erb index 3daa091db81d6b520182cfca99421aefb73300d6..79e72ca16c6fbb7d1cdcbd16dd461251830cd53d 100644 --- a/app/views/notify/note_merge_request_email.text.erb +++ b/app/views/notify/note_merge_request_email.text.erb @@ -1,9 +1,9 @@ -New comment for Merge Request <%= @merge_request.id %> +New comment for Merge Request <%= @merge_request.iid %> <%= url_for(project_merge_request_url(@merge_request.target_project, @merge_request, anchor: "note_#{@note.id}")) %> - + <%= @note.author_name %> - + <%= @note.note %> diff --git a/app/views/notify/note_wall_email.text.erb b/app/views/notify/note_wall_email.text.erb index 97910d5eb7988b602e3f2e9407e53b2cf85a0181..ee0dc3db1dbb54e194febce8762477e8447a7210 100644 --- a/app/views/notify/note_wall_email.text.erb +++ b/app/views/notify/note_wall_email.text.erb @@ -1,7 +1,7 @@ New message on the project wall <%= @note.project %> <%= url_for(project_wall_url(@note.project, anchor: "note_#{@note.id}")) %> - + <%= @note.author_name %> diff --git a/app/views/notify/project_access_granted_email.html.haml b/app/views/notify/project_access_granted_email.html.haml index b4b44eafe2c0cfd4c39b1f9e5911e671ad785f0b..ce34f825358a45c4e1def28363bba54fbe2c791a 100644 --- a/app/views/notify/project_access_granted_email.html.haml +++ b/app/views/notify/project_access_granted_email.html.haml @@ -1,5 +1,5 @@ %p - = "You have been granted #{@users_project.project_access_human} access to project" + = "You have been granted #{@users_project.human_access} access to project" %p = link_to project_url(@project) do = @project.name_with_namespace diff --git a/app/views/notify/project_access_granted_email.text.erb b/app/views/notify/project_access_granted_email.text.erb index 077c3b8a7dec82026dc08a71431d52876d68ead0..66c57def37564f1d3bc041da9751dd4abc5c9092 100644 --- a/app/views/notify/project_access_granted_email.text.erb +++ b/app/views/notify/project_access_granted_email.text.erb @@ -1,4 +1,4 @@ -You have been granted <%= @users_project.project_access_human %> access to project <%= @project.name_with_namespace %> +You have been granted <%= @users_project.human_access %> access to project <%= @project.name_with_namespace %> <%= url_for(project_url(@project)) %> diff --git a/app/views/notify/project_was_moved_email.text.erb b/app/views/notify/project_was_moved_email.text.erb index 9c41073fddc93cf9e87963c5716466a692ee6d5b..7889c7b9cc4297a32f2d47a93e3ea7660ca630f5 100644 --- a/app/views/notify/project_was_moved_email.text.erb +++ b/app/views/notify/project_was_moved_email.text.erb @@ -1,8 +1,8 @@ Project was moved to another location - + The project is now located under <%= project_url(@project) %> - + To update the remote url in your local repository run: git remote set-url origin <%= @project.ssh_url_to_repo %> diff --git a/app/views/notify/reassigned_issue_email.html.haml b/app/views/notify/reassigned_issue_email.html.haml index 6d332af3d7008242594bd74658715685b17fc0d8..b0edff44ce6009dd7641d7f006879270694a0ff1 100644 --- a/app/views/notify/reassigned_issue_email.html.haml +++ b/app/views/notify/reassigned_issue_email.html.haml @@ -1,5 +1,5 @@ %p - = "Reassigned Issue ##{@issue.id}" + = "Reassigned Issue ##{@issue.iid}" = link_to_gfm truncate(@issue.title, length: 30), project_issue_url(@issue.project, @issue) %p Assignee changed diff --git a/app/views/notify/reassigned_issue_email.text.erb b/app/views/notify/reassigned_issue_email.text.erb index 867c4d01da51c529c9b013ae1b0d7a8dfbe7dfa0..bc0d0567922f40fcfbe424c04d7c9e221f8824bb 100644 --- a/app/views/notify/reassigned_issue_email.text.erb +++ b/app/views/notify/reassigned_issue_email.text.erb @@ -1,5 +1,5 @@ -Reassigned Issue <%= @issue.id %> +Reassigned Issue <%= @issue.iid %> <%= url_for(project_issue_url(@issue.project, @issue)) %> - + Assignee changed <%= "from #{@previous_assignee.name}" if @previous_assignee %> to <%= @issue.assignee_name %> diff --git a/app/views/notify/reassigned_merge_request_email.html.haml b/app/views/notify/reassigned_merge_request_email.html.haml index 314b2882672203cbcb09e8a9a531b3de82383864..3a615f86e69edce9ffc5dba7bc6e29f042bb2ecb 100644 --- a/app/views/notify/reassigned_merge_request_email.html.haml +++ b/app/views/notify/reassigned_merge_request_email.html.haml @@ -1,5 +1,5 @@ %p - = "Reassigned Merge Request !#{@merge_request.id}" + = "Reassigned Merge Request !#{@merge_request.iid}" = link_to_gfm truncate(@merge_request.title, length: 30), project_merge_request_url(@merge_request.target_project, @merge_request) %p Assignee changed diff --git a/app/views/notify/reassigned_merge_request_email.text.erb b/app/views/notify/reassigned_merge_request_email.text.erb index 05a9797165da850a77eef3ba52a6b6174801512d..eecf055ff6f800821168e293d9ec1088a9136f62 100644 --- a/app/views/notify/reassigned_merge_request_email.text.erb +++ b/app/views/notify/reassigned_merge_request_email.text.erb @@ -1,7 +1,7 @@ -Reassigned Merge Request <%= @merge_request.id %> +Reassigned Merge Request <%= @merge_request.iid %> <%= url_for(project_merge_request_url(@merge_request.target_project, @merge_request)) %> - + Assignee changed <%= "from #{@previous_assignee.name}" if @previous_assignee %> to <%= @merge_request.assignee_name %> diff --git a/app/views/profiles/account.html.haml b/app/views/profiles/account.html.haml index d711d55ec99da82b685ba431bee71ca3acc16a29..8f43db662498e78c516a77eb1d4c44d35b64cfdb 100644 --- a/app/views/profiles/account.html.haml +++ b/app/views/profiles/account.html.haml @@ -5,91 +5,128 @@ - if current_user.ldap_user? Some options are unavailable for LDAP accounts %hr -- unless current_user.ldap_user? - - if Gitlab.config.omniauth.enabled - %fieldset - %legend Social Accounts - .oauth_select_holder - %p.hint Tip: Click on icon to activate signin with one of the following services - - enabled_social_providers.each do |provider| - %span{class: oauth_active_class(provider) } - = link_to authbutton(provider, 32), omniauth_authorize_path(User, provider) - %fieldset.update-password - %legend Password - = form_for @user, url: update_password_profile_path, method: :put do |f| - .padded - %p.slead After a successful password update you will be redirected to login page where you should login with your new password - -if @user.errors.any? - .alert.alert-error - %ul - - @user.errors.full_messages.each do |msg| - %li= msg +.row + .span2 + %ul.nav.nav-pills.nav-stacked.nav-stacked-menu + %li.active + = link_to '#tab-token', 'data-toggle' => 'tab' do + Private Token + %li + = link_to '#tab-password', 'data-toggle' => 'tab' do + Password - .control-group - = f.label :password - .controls= f.password_field :password, required: true - .control-group - = f.label :password_confirmation - .controls - = f.password_field :password_confirmation, required: true - .control-group - .controls - = f.submit 'Save password', class: "btn btn-save" + - if show_profile_social_tab? + %li + = link_to '#tab-social', 'data-toggle' => 'tab' do + Social Accounts + - if show_profile_username_tab? + %li + = link_to '#tab-username', 'data-toggle' => 'tab' do + Change Username + - if show_profile_remove_tab? + %li + = link_to '#tab-remove', 'data-toggle' => 'tab' do + Remove Account + .span10 + .tab-content + .tab-pane.active#tab-token + %fieldset.update-token + %legend + Private token + %span.cred.pull-right + keep it secret! + %div + = form_for @user, url: reset_private_token_profile_path, method: :put do |f| + .data + %p.slead + Your private token is used to access application resources without authentication. + %br + It can be used for atom feeds or the API. + %p.cgray + - if current_user.private_token + = text_field_tag "token", current_user.private_token, class: "input-xxlarge large_text input-xpadding" + = f.submit 'Reset', confirm: "Are you sure?", class: "btn btn-primary btn-build-token" + - else + %span You don`t have one yet. Click generate to fix it. + = f.submit 'Generate', class: "btn success btn-build-token" -%fieldset.update-token - %legend - Private token - %span.cred.pull-right - keep it secret! - .padded - = form_for @user, url: reset_private_token_profile_path, method: :put do |f| - .data - %p.slead - Your private token is used to access application resources without authentication. - %br - It can be used for atom feeds or the API. - %p.cgray - - if current_user.private_token - = text_field_tag "token", current_user.private_token, class: "input-xxlarge large_text input-xpadding" - = f.submit 'Reset', confirm: "Are you sure?", class: "btn btn-primary btn-build-token" - - else - %span You don`t have one yet. Click generate to fix it. - = f.submit 'Generate', class: "btn success btn-build-token" + .tab-pane#tab-password + %fieldset.update-password + %legend Password + = form_for @user, url: update_password_profile_path, method: :put do |f| + %div + %p.slead After a successful password update you will be redirected to login page where you should login with your new password + -if @user.errors.any? + .alert.alert-error + %ul + - @user.errors.full_messages.each do |msg| + %li= msg + .control-group + = f.label :password + .controls= f.password_field :password, required: true + .control-group + = f.label :password_confirmation + .controls + = f.password_field :password_confirmation, required: true + .control-group + .controls + = f.submit 'Save password', class: "btn btn-save" + - if show_profile_social_tab? + .tab-pane#tab-social + %fieldset + %legend Social Accounts + .oauth_select_holder + %p.hint Tip: Click on icon to activate signin with one of the following services + - enabled_social_providers.each do |provider| + %span{class: oauth_active_class(provider) } + = link_to authbutton(provider, 32), omniauth_authorize_path(User, provider) -- if current_user.can_change_username? - %fieldset.update-username - %legend - Username - %small.cred.pull-right - Changing your username can have unintended side effects! - = form_for @user, url: update_username_profile_path, method: :put, remote: true do |f| - .padded - = f.label :username - .controls - = f.text_field :username, required: true - - %span.loading-gif.hide= image_tag "ajax_loader.gif" - %span.update-success.cgreen.hide - %i.icon-ok - Saved - %span.update-failed.cred.hide - %i.icon-remove - Failed - %ul.cred - %li This will change the web URL for personal projects. - %li This will change the git path to repositories for personal projects. - .controls - = f.submit 'Save username', class: "btn btn-save" + - if show_profile_username_tab? + .tab-pane#tab-username + %fieldset.update-username + %legend + Username + %small.cred.pull-right + Changing your username can have unintended side effects! + = form_for @user, url: update_username_profile_path, method: :put, remote: true do |f| + %div + .control-group + = f.label :username + .controls + = f.text_field :username, required: true + + %span.loading-gif.hide= image_tag "ajax_loader.gif" + %span.update-success.cgreen.hide + %i.icon-ok + Saved + %span.update-failed.cred.hide + %i.icon-remove + Failed + %ul.cred + %li This will change the web URL for personal projects. + %li This will change the git path to repositories for personal projects. + .controls + = f.submit 'Save username', class: "btn btn-save" -- if gitlab_config.signup_enabled - %fieldset.remove-account - %legend - Remove account - %small.cred.pull-right - Before removing the account you must remove all projects! - = link_to 'Delete account', user_registration_path, confirm: "REMOVE #{current_user.name}? Are you sure?", method: :delete, class: "btn btn-remove delete-key btn-small pull-right" + - if show_profile_remove_tab? + .tab-pane#tab-remove + %fieldset.remove-account + %legend + Remove account + %div + %p Deleting an account has the following effects: + %ul + %li All user content like authored issues, snippets, comments will be removed + - rp = current_user.personal_projects.count + - unless rp.zero? + %li #{pluralize rp, 'personal project'} will be removed and cannot be restored + - if current_user.solo_owned_groups.present? + %li + Next groups will be abandoned. You should transfer or remove them: + %strong #{current_user.solo_owned_groups.map(&:name).join(', ')} + = link_to 'Delete account', user_registration_path, confirm: "REMOVE #{current_user.name}? Are you sure?", method: :delete, class: "btn btn-remove" diff --git a/app/views/profiles/design.html.haml b/app/views/profiles/design.html.haml index 75f00ab10a2a136a176dafa7be070485e2c8ca00..0d8075b7d43209d67a02913b40844b46c558a6d8 100644 --- a/app/views/profiles/design.html.haml +++ b/app/views/profiles/design.html.haml @@ -8,9 +8,6 @@ %fieldset.application-theme %legend Application theme - .update-feedback.hide - %i.icon-ok - Saved .themes_opts = label_tag do .prev.default @@ -42,9 +39,6 @@ %fieldset.code-preview-theme %legend Code preview theme - .update-feedback.hide - %i.icon-ok - Saved .code_highlight_opts - color_schemes.each do |color_scheme_id, color_scheme| = label_tag do diff --git a/app/views/profiles/groups/index.html.haml b/app/views/profiles/groups/index.html.haml index d3ff233bf12d93318f3e6123e41429068eadde35..6fc27be5db4399d051869fc1100371d79f113b3b 100644 --- a/app/views/profiles/groups/index.html.haml +++ b/app/views/profiles/groups/index.html.haml @@ -29,7 +29,10 @@ = link_to group, class: 'group-name' do %strong= group.name - as #{user_group.human_access} + as + %strong #{user_group.human_access} + %div.light + #{pluralize(group.projects.count, "project")}, #{pluralize(group.users.count, "user")} = paginate @user_groups diff --git a/app/views/profiles/keys/_form.html.haml b/app/views/profiles/keys/_form.html.haml index 3a0b6cd48a1bbe3d497a35086afe78328a1b5aa7..158979c0ee5eae93e425bc9a7e113379961e0360 100644 --- a/app/views/profiles/keys/_form.html.haml +++ b/app/views/profiles/keys/_form.html.haml @@ -13,7 +13,7 @@ = f.label :key .controls %p.light - Paste your public key here. Read more about how generate it #{link_to "here", help_ssh_path} + Paste your public key here. Read more about how to generate a key on #{link_to "the SSH help page", help_ssh_path}. = f.text_area :key, class: "input-xxlarge thin_area" diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml index 356d5499f07ce1c5842cfe2c4860e046fd65b92b..8353b2f5f2306448c821f3a2761e2a6650415c89 100644 --- a/app/views/profiles/notifications/show.html.haml +++ b/app/views/profiles/notifications/show.html.haml @@ -56,12 +56,3 @@ - @users_projects.each do |users_project| - notification = Notification.new(users_project) = render 'settings', type: 'project', membership: users_project, notification: notification - - -.save-status-fixed - %span.update-success.cgreen.hide - %i.icon-ok - Saved - %span.update-failed.cred.hide - %i.icon-remove - Failed diff --git a/app/views/profiles/notifications/update.js.haml b/app/views/profiles/notifications/update.js.haml index 88e74d50671d834a7e2159568447c44916e44893..84c6ab25599e4356f30a14c33264d884a51b3e07 100644 --- a/app/views/profiles/notifications/update.js.haml +++ b/app/views/profiles/notifications/update.js.haml @@ -1,6 +1,6 @@ - if @saved :plain - $('.save-status-fixed .update-success').showAndHide(); + new Flash("Notification settings saved", "notice") - else :plain - $('.save-status-fixed .update-failed').showAndHide(); + new Flash("Failed to save new settings", "alert") diff --git a/app/views/projects/_clone_panel.html.haml b/app/views/projects/_clone_panel.html.haml index 4aee2b16bdb4a8195d648e0f0269d57f7fb3aa19..7228c760d27cdceb4cd4ba274a581078507f63be 100644 --- a/app/views/projects/_clone_panel.html.haml +++ b/app/views/projects/_clone_panel.html.haml @@ -37,8 +37,19 @@ %li = link_to new_project_snippet_path(@project), title: "New Snippet" do Snippet + - if can? current_user, :push_code, @project + %li.divider + %li + = link_to new_project_branch_path(@project) do + %i.icon-code-fork + Git branch + %li + = link_to new_project_tag_path(@project) do + %i.icon-tag + Git tag + - if can?(current_user, :admin_team_member, @project) %li.divider %li - = link_to new_project_team_member_path(@project), title: "New Team member" do - Team member + = link_to new_project_team_member_path(@project), title: "New project member" do + Project member diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index 026948aa0275fea71d540880d98ea0987b880ec8..b83925257918ee631f1d464824941a701c16e8f0 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -13,6 +13,10 @@ = link_to archive_project_repository_path(@project, ref: branch.name), class: 'btn grouped btn-small' do %i.icon-download-alt Download + = link_to project_compare_index_path(@project, from: branch.name, to: branch.name), class: 'btn grouped btn-small', title: "Compare" do + %i.icon-copy + Compare + - if can?(current_user, :admin_project, @project) && branch.name != @repository.root_ref = link_to project_branch_path(@project, branch.name), class: 'btn grouped btn-small remove-row', method: :delete, confirm: 'Removed branch cannot be restored. Are you sure?', remote: true do %i.icon-trash diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml index eb5bdb886e29e39894684887d140f92c85de77f1..d70fd96accdf22d8f5872b5b9073e45a02fa91fb 100644 --- a/app/views/projects/commits/_commit.html.haml +++ b/app/views/projects/commits/_commit.html.haml @@ -1,21 +1,19 @@ %li.commit - .browse_code_link_holder - %p - %strong= link_to "Browse Code »", project_tree_path(project, commit), class: "right" - %p + .commit-row-title = link_to commit.short_id(8), project_commit_path(project, commit), class: "commit_short_id" - = commit_author_link(commit, avatar: true, size: 24) - = link_to_gfm truncate(commit.title, length: 70), project_commit_path(project, commit.id), class: "row_title" - - %time.committed_ago{ datetime: commit.committed_date, title: commit.committed_date.stamp("Aug 21, 2011 9:23pm") } - = time_ago_in_words(commit.committed_date) - ago - - - %span.notes_count + = link_to_gfm truncate(commit.title, length: 70), project_commit_path(project, commit.id), class: "commit-row-message" + = link_to "Browse Code »", project_tree_path(project, commit), class: "pull-right" + .notes_count - notes = project.notes.for_commit_id(commit.id) - if notes.any? %span.badge.badge-info %i.icon-comment = notes.count + + .commit-row-info + = commit_author_link(commit, avatar: true, size: 16) + %time.committed_ago{ datetime: commit.committed_date, title: commit.committed_date.stamp("Aug 21, 2011 9:23pm") } + = time_ago_in_words(commit.committed_date) + ago + diff --git a/app/views/projects/commits/_commits.html.haml b/app/views/projects/commits/_commits.html.haml index fd0616f9acc4dfe117d82abd494a6655c3c54e51..b6404778073e5bb3ef3dd8d9318ac8d4f314c83b 100644 --- a/app/views/projects/commits/_commits.html.haml +++ b/app/views/projects/commits/_commits.html.haml @@ -1,8 +1,11 @@ - @commits.group_by { |c| c.committed_date.to_date }.sort.reverse.each do |day, commits| - .ui-box.small-box - .title - %i.icon-calendar - %span= day.stamp("28 Aug, 2010") - .pull-right - %small= pluralize(commits.count, 'commit') - %ul.well-list= render commits, project: @project + .row.commits-row + .span2 + %h4 + %i.icon-calendar + %span= day.stamp("28 Aug, 2010") + %p= pluralize(commits.count, 'commit') + .span10 + %ul.well-list + = render commits, project: @project + %hr.lists-separator diff --git a/app/views/projects/commits/_diffs.html.haml b/app/views/projects/commits/_diffs.html.haml index 5a3a9bd16aff682d30a027d27a92df191203d534..c51f1b6eff50504cb5acd897eb3caa12e6628ebe 100644 --- a/app/views/projects/commits/_diffs.html.haml +++ b/app/views/projects/commits/_diffs.html.haml @@ -1,11 +1,25 @@ +- @suppress_diff ||= @suppress_diff || @force_suppress_diff - if @suppress_diff .alert.alert-block %p - %strong Warning! Large commit with more than #{Commit::DIFF_SAFE_SIZE} files changed. - %p To preserve performance the diff is not shown. + %strong Warning! This is a large diff. %p - But if you still want to see the diff - = link_to "click this link", project_commit_path(project, @commit, force_show_diff: true), class: "underlined_link" + To preserve performance the diff is not shown. + - if current_controller?(:commit) or current_controller?(:merge_requests) + Please, download the diff as + - if current_controller?(:commit) + = link_to "plain diff", project_commit_path(@project, @commit, format: :diff), class: "underlined_link" + or + = link_to "email patch", project_commit_path(@project, @commit, format: :patch), class: "underlined_link" + - else + = link_to "plain diff", project_merge_request_path(@project, @merge_request, format: :diff), class: "underlined_link" + or + = link_to "email patch", project_merge_request_path(@project, @merge_request, format: :patch), class: "underlined_link" + instead. + - unless @force_suppress_diff + %p + If you still want to see the diff + = link_to "click this link", url_for(force_show_diff: true), class: "underlined_link" %p.commit-stat-summary Showing diff --git a/app/views/projects/commits/_head.html.haml b/app/views/projects/commits/_head.html.haml index c16abac7f171c0860f706348c9899ac619f2424f..624604142b1c286c60fd2ca091c94c3ff3894d3a 100644 --- a/app/views/projects/commits/_head.html.haml +++ b/app/views/projects/commits/_head.html.haml @@ -4,7 +4,7 @@ = nav_link(controller: [:commit, :commits]) do = link_to 'Commits', project_commits_path(@project, @repository.root_ref) = nav_link(controller: :compare) do - = link_to 'Compare', project_compare_index_path(@project) + = link_to 'Compare', project_compare_index_path(@project, from: @repository.root_ref, to: @ref || @repository.root_ref) = nav_link(html_options: {class: branches_tab_class}) do = link_to recent_project_branches_path(@project) do diff --git a/app/views/projects/commits/_image.html.haml b/app/views/projects/commits/_image.html.haml index db02fa333b93779587b9414e406df83f10a6c902..73f87289d3db1486de190791dbb764709fd09d21 100644 --- a/app/views/projects/commits/_image.html.haml +++ b/app/views/projects/commits/_image.html.haml @@ -9,7 +9,7 @@ %div.two-up.view %span.wrap .frame.deleted - %a{href: project_tree_path(@project, tree_join(@commit.id, diff.old_path))} + %a{href: project_blob_path(@project, tree_join(@commit.parent_id, diff.old_path))} %img{src: "data:#{old_file.mime_type};base64,#{Base64.encode64(old_file.data)}"} %p.image-info.hide %span.meta-filesize= "#{number_to_human_size old_file.size}" @@ -21,7 +21,7 @@ %span.meta-height %span.wrap .frame.added - %a{href: project_tree_path(@project, tree_join(@commit.id, diff.new_path))} + %a{href: project_blob_path(@project, tree_join(@commit.id, diff.new_path))} %img{src: "data:#{file.mime_type};base64,#{Base64.encode64(file.data)}"} %p.image-info.hide %span.meta-filesize= "#{number_to_human_size file.size}" diff --git a/app/views/projects/commits/_inline_commit.html.haml b/app/views/projects/commits/_inline_commit.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..5be8460e06130ed38abf51f7d1f07e442f99f577 --- /dev/null +++ b/app/views/projects/commits/_inline_commit.html.haml @@ -0,0 +1,9 @@ +%li.commit.inline-commit + .commit-row-title + = link_to commit.short_id(8), project_commit_path(project, commit), class: "commit_short_id" + + = link_to_gfm truncate(commit.title, length: 40), project_commit_path(project, commit.id), class: "commit-row-message" + %time.committed_ago{ datetime: commit.committed_date, title: commit.committed_date.stamp("Aug 21, 2011 9:23pm") } + = time_ago_in_words(commit.committed_date) + ago + diff --git a/app/views/projects/compare/_form.html.haml b/app/views/projects/compare/_form.html.haml index ee733f8095493897aeee232fa03c93be80ebe5ef..ca1ea4073e6ea4b51fc453d1241a93cb27d20651 100644 --- a/app/views/projects/compare/_form.html.haml +++ b/app/views/projects/compare/_form.html.haml @@ -1,29 +1,21 @@ -%div - - unless params[:to] - %p.slead - Fill input field with commit id like - %code.label-branch 4eedf23 - or branch/tag name like - %code.label-branch master - and press compare button for commits list, code diff. - - %br - - = form_tag project_compare_index_path(@project), method: :post do - .clearfix - .pull-left - - if params[:to] && params[:from] - = link_to 'switch', {from: params[:to], to: params[:from]}, {class: 'commits-compare-switch has_tooltip', title: 'Switch base of comparison'} - = text_field_tag :from, params[:from], placeholder: "master", class: "input-xlarge input-xpadding" - = "..." - = text_field_tag :to, params[:to], placeholder: "aa8b4ef", class: "input-xlarge input-xpadding" - .pull-left - - = submit_tag "Compare", class: "btn btn-create commits-compare-btn" - - if @refs_are_same - .alert - %span Refs are the same - += form_tag project_compare_index_path(@project), method: :post do + .clearfix + .pull-left + - if params[:to] && params[:from] + = link_to 'switch', {from: params[:to], to: params[:from]}, {class: 'commits-compare-switch has_tooltip', title: 'Switch base of comparison'} + .input-prepend + %span.add-on.input-xpadding from + = text_field_tag :from, params[:from], class: "span3 input-xpadding" + = "..." + .input-prepend + %span.add-on.input-xpadding to + = text_field_tag :to, params[:to], class: "span3 input-xpadding" + .pull-left + + = submit_tag "Compare", class: "btn btn-create commits-compare-btn" + - if compare_to_mr_button? + = link_to compare_mr_path, class: 'prepend-left-10' do + %strong Make a merge request :javascript diff --git a/app/views/projects/compare/index.html.haml b/app/views/projects/compare/index.html.haml index 8f108ed0b08932898859200945c0075536b31f29..4745bfbeaaf061e5a1f896ed2864f0ac76b2be73 100644 --- a/app/views/projects/compare/index.html.haml +++ b/app/views/projects/compare/index.html.haml @@ -2,5 +2,15 @@ %h3.page-title Compare View +%p.slead + Compare branches, tags or commit ranges. + %br + Fill input field with commit id like + %code.label-branch 4eedf23 + or branch/tag name like + %code.label-branch master + and press compare button for the commits list and a code diff. + %br + Changes are shown from the version in the first field to the version in the second field. = render "form" diff --git a/app/views/projects/compare/show.html.haml b/app/views/projects/compare/show.html.haml index af9273f9315b5eee7eec8ba9dbe40891eaea3485..4be62d3691742dcfad2c00f6f616b99117d6b4fa 100644 --- a/app/views/projects/compare/show.html.haml +++ b/app/views/projects/compare/show.html.haml @@ -20,3 +20,16 @@ - unless @diffs.empty? %h4 Diff = render "projects/commits/diffs", diffs: @diffs, project: @project +- else + .light-well + %center + %h4 + There isn't anything to compare. + %p.slead + - if params[:to] == params[:from] + You'll need to use different branch names to get a valid comparison. + - else + %span.label-branch #{params[:from]} + and + %span.label-branch #{params[:to]} + are the same. diff --git a/app/views/projects/edit_tree/show.html.haml b/app/views/projects/edit_tree/show.html.haml index 06396ba992b4f5f8ca536280294ec240fb7afaed..b1fb4f8637ddd1f53095cef60492bda37b5846fd 100644 --- a/app/views/projects/edit_tree/show.html.haml +++ b/app/views/projects/edit_tree/show.html.haml @@ -31,7 +31,7 @@ = link_to "Cancel", project_blob_path(@project, @id), class: "btn btn-cancel", confirm: leave_edit_message :javascript - ace.config.set("modePath", "#{Gitlab::Application.config.assets.prefix}/ace-src-noconflict") + ace.config.set("modePath", gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}/ace-src-noconflict") var ace_mode = "#{@blob.language.try(:ace_mode)}"; var editor = ace.edit("editor"); if (ace_mode) { diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml index 99e334cc6b8225b09b6f904b6c62e89b003e7434..001857cefda2c5f3faf72c4eb8fe8d16cc258e64 100644 --- a/app/views/projects/empty.html.haml +++ b/app/views/projects/empty.html.haml @@ -29,7 +29,8 @@ touch README git add README git commit -m 'first commit' - git remote add origin #{@project.url_to_repo} + %span.clone= "git remote add origin #{@project.url_to_repo}" + :preserve git push -u origin master %fieldset @@ -37,7 +38,8 @@ %pre.dark :preserve cd existing_git_repo - git remote add origin #{@project.url_to_repo} + %span.clone= "git remote add origin #{@project.url_to_repo}" + :preserve git push -u origin master - if can? current_user, :remove_project, @project diff --git a/app/views/projects/issues/_form.html.haml b/app/views/projects/issues/_form.html.haml index 957cc5a7e2d9ed49116b9bd3fab775fda935731b..6acad9134d1e67eead4fba2bf79d76fb119834d4 100644 --- a/app/views/projects/issues/_form.html.haml +++ b/app/views/projects/issues/_form.html.haml @@ -67,8 +67,8 @@ event.preventDefault(); } }) - .bind( "click", function( event ) { - $( this ).autocomplete("search", ""); + .bind("click", function(event) { + $(this).autocomplete("search", ""); }) .autocomplete({ minLength: 0, diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml index 0f3c4f752eb69347df667e50b7937d7128a43cf2..b9a2c18efdc78bf99a02e08384bc5e8e1277fcf7 100644 --- a/app/views/projects/issues/_issue.html.haml +++ b/app/views/projects/issues/_issue.html.haml @@ -4,7 +4,7 @@ = check_box_tag dom_id(issue,"selected"), nil, false, 'data-id' => issue.id, class: "selected_issue", disabled: !can?(current_user, :modify_issue, issue) .issue-title - %span.light= "##{issue.id}" + %span.light= "##{issue.iid}" = link_to_gfm truncate(issue.title, length: 100), project_issue_path(issue.project, issue), class: "row_title" .issue-info diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index fc6faa9c19d175be36dc6c8e33e4ac2f0e5c381a..d36a831432d7e95b3fb2c2033b131cc66520f145 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -1,5 +1,5 @@ %h3.page-title - Issue ##{@issue.id} + Issue ##{@issue.iid} %small created at diff --git a/app/views/projects/merge_requests/_form.html.haml b/app/views/projects/merge_requests/_form.html.haml index 5e94f1fdf18ae37f3ef88302c4bc82a2ae7a95f1..ce72756303e76ce190f710720ac6fc65a482c67b 100644 --- a/app/views/projects/merge_requests/_form.html.haml +++ b/app/views/projects/merge_requests/_form.html.haml @@ -5,41 +5,32 @@ - @merge_request.errors.full_messages.each do |msg| %li= msg - %h3.page-title - Branches .merge-request-branches - .row - .span5 - .ui-box - .title From - .ui-box-head - Project: - = f.select(:source_project_id,[[@merge_request.source_project.path_with_namespace,@merge_request.source_project.id]] , {}, {class: 'source_project chosen span4'}) - .prepend-top-10 + .row + .span5 + .clearfix + .pull-left + = f.select(:source_project_id,[[@merge_request.source_project.path_with_namespace,@merge_request.source_project.id]] , {}, {class: 'source_project chosen span3'}) + .pull-left + %i.icon-code-fork - Branch: - = f.select(:source_branch, @merge_request.source_project.repository.branch_names, { include_blank: "Select branch" }, {class: 'source_branch chosen span3'}) - .mr_source_commit.prepend-top-10 - .span2 - %h1.merge-request-angle - %i.icon-angle-right - .span5 - .ui-box - .title To - .ui-box-head - - projects = @project.forked_from_project.nil? ? [@project] : [ @project,@project.forked_from_project] - Project: - = f.select(:target_project_id, projects.map { |proj| [proj.path_with_namespace,proj.id] }, {include_blank: "Select Target Project" }, {class: 'target_project chosen span4'}) - .prepend-top-10 + = f.select(:source_branch, @merge_request.source_project.repository.branch_names, { include_blank: "Select branch" }, {class: 'source_branch chosen span2'}) + .mr_source_commit.prepend-top-10 + .span2 + %h2.merge-request-angle.light + %i.icon-long-arrow-right + .span5 + .clearfix + .pull-left + - projects = @project.forked_from_project.nil? ? [@project] : [ @project,@project.forked_from_project] + = f.select(:target_project_id, projects.map { |proj| [proj.path_with_namespace,proj.id] }, {include_blank: "Select Target Project" }, {class: 'target_project chosen span3'}) + .pull-left + %i.icon-code-fork - Branch: - = f.select(:target_branch, @target_branches, { include_blank: "Select branch" }, {class: 'target_branch chosen span3'}) - .mr_target_commit.prepend-top-10 + = f.select(:target_branch, @target_branches, { include_blank: "Select branch" }, {class: 'target_branch chosen span2'}) + .mr_target_commit.prepend-top-10 %hr - - %h3.page-title - Details .merge-request-form-info .control-group = f.label :title do @@ -56,6 +47,12 @@ %i.icon-time Milestone .controls= f.select(:milestone_id, @project.milestones.active.all.map {|p| [ p.title, p.id ] }, { include_blank: "Select milestone" }, {class: 'chosen'}) + .control-group + = f.label :description, "Description" + .controls + = f.text_area :description, class: "input-xxlarge js-gfm-input", rows: 14 + %p.hint Description is parsed with #{link_to "GitLab Flavored Markdown", help_markdown_path, target: '_blank'}. + .form-actions - if @merge_request.new_record? diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index 276436a9c8e398529b90b002147893aac6dc3f8a..933d78bcbfbd5e12fe3b70e6e85470130d7017e5 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -1,6 +1,6 @@ %li{ class: mr_css_classes(merge_request) } .merge-request-title - %span.light= "##{merge_request.id}" + %span.light= "##{merge_request.iid}" = link_to_gfm truncate(merge_request.title, length: 80), project_merge_request_path(merge_request.target_project, merge_request), class: "row_title" - if merge_request.merged? %small.pull-right diff --git a/app/views/projects/merge_requests/show/_mr_box.html.haml b/app/views/projects/merge_requests/show/_mr_box.html.haml index 594f4061c236bdd7cccee2e09e5aa1143e7e5a47..1f750e22c6571439914c2f3e336309f7ab5033dd 100644 --- a/app/views/projects/merge_requests/show/_mr_box.html.haml +++ b/app/views/projects/merge_requests/show/_mr_box.html.haml @@ -21,6 +21,12 @@ %strong= link_to_gfm truncate(milestone.title, length: 20), project_milestone_path(milestone.project, milestone) + - if @merge_request.description.present? + .ui-box-bottom + .wiki + = preserve do + = markdown @merge_request.description + - if @merge_request.closed? .ui-box-bottom.alert-error %span @@ -33,4 +39,10 @@ %i.icon-ok Merged by #{link_to_member(@project, @merge_request.merge_event.author)} #{time_ago_in_words(@merge_request.merge_event.created_at)} ago. - + - if !@closes_issues.empty? && @merge_request.opened? + .ui-box-bottom.alert-info + %span + %i.icon-ok + Accepting this merge request will close #{@closes_issues.size == 1 ? 'issue' : 'issues'} + = succeed '.' do + != gfm(@closes_issues.map { |i| "##{i.iid}" }.to_sentence) diff --git a/app/views/projects/merge_requests/show/_mr_title.html.haml b/app/views/projects/merge_requests/show/_mr_title.html.haml index 7dfc995121f7a0e19079578f9fdff9eaaab5f3ca..096a91676455682750feb3d7984fadf7ff28ff44 100644 --- a/app/views/projects/merge_requests/show/_mr_title.html.haml +++ b/app/views/projects/merge_requests/show/_mr_title.html.haml @@ -1,5 +1,5 @@ %h3.page-title - = "Merge Request ##{@merge_request.id}:" + = "Merge Request ##{@merge_request.iid}:" -if @merge_request.for_fork? %span.label-branch diff --git a/app/views/projects/milestones/_issues.html.haml b/app/views/projects/milestones/_issues.html.haml index 983692399cac39c40a504d7e7db65e89d66d5a3a..bf81cfda45fdf12b1b8fabdb0f5e6392ea0b8ecc 100644 --- a/app/views/projects/milestones/_issues.html.haml +++ b/app/views/projects/milestones/_issues.html.haml @@ -4,7 +4,7 @@ - issues.each do |issue| %li = link_to [@project, issue] do - %span.badge{class: issue.closed? ? 'badge-important' : 'badge-info'} ##{issue.id} + %span.badge{class: issue.closed? ? 'badge-important' : 'badge-info'} ##{issue.iid} = link_to_gfm truncate(issue.title, length: 40), [@project, issue] - if issue.assignee .pull-right diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index 8a5c2a938b118ebe722f03cd9ba054d228d2bf04..b755a8134191c23e27085346a340664c5f5a0806 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -8,7 +8,10 @@ = link_to edit_project_milestone_path(@project, @milestone), class: "btn grouped" do %i.icon-edit Edit - = link_to 'Close Milestone', project_milestone_path(@project, @milestone, milestone: {state_event: :close }), method: :put, class: "btn btn-remove" + - if @milestone.active? + = link_to 'Close Milestone', project_milestone_path(@project, @milestone, milestone: {state_event: :close }), method: :put, class: "btn btn-remove" + - else + = link_to 'Reopen Milestone', project_milestone_path(@project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn" - if @milestone.issues.any? && @milestone.can_be_closed? .alert.alert-success diff --git a/app/views/projects/network/_head.html.haml b/app/views/projects/network/_head.html.haml index ed9fcaace250611761f5594f6a775eff7e568cfe..2790ed6f59483725687039d95ef88a537ba4fcbb 100644 --- a/app/views/projects/network/_head.html.haml +++ b/app/views/projects/network/_head.html.haml @@ -4,7 +4,7 @@ .pull-left = form_tag project_network_path(@project, @id), method: :get do |f| .control-group - = label_tag :filter_ref, "Show only selected ref", class: 'control-label light' + = label_tag :filter_ref, "Begin with the selected commit", class: 'control-label light' .controls = check_box_tag :filter_ref, 1, @options[:filter_ref] - @options.each do |key, value| @@ -15,9 +15,9 @@ .control-group = label_tag :search , "Looking for commit:", class: 'control-label light' .controls - = text_field_tag :q, @options[:q], placeholder: "Input SHA", class: "search-input input-xlarge" + = text_field_tag :extended_sha1, @options[:extended_sha1], placeholder: "Input an extended SHA1 syntax", class: "search-input input-xlarge" = button_tag type: 'submit', class: 'btn vtop' do %i.icon-search - @options.each do |key, value| - = hidden_field_tag(key, value, id: nil) unless key == "q" + = hidden_field_tag(key, value, id: nil) unless key == "extended_sha1" diff --git a/app/views/projects/network/show.json.erb b/app/views/projects/network/show.json.erb index 9a62cdb3dc9078ee840379e24dcc5b2e1cf406ed..f0bedcf2d352918e471d6e128286866314e43597 100644 --- a/app/views/projects/network/show.json.erb +++ b/app/views/projects/network/show.json.erb @@ -13,7 +13,7 @@ }, time: c.time, space: c.spaces.first, - refs: get_refs(c), + refs: get_refs(@graph.repo, c), id: c.sha, date: c.date, message: c.message, diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index 736da85952a688caec9b8063be1eb50e182d6fab..ab8efe5e3e140be7354a1be34b41136e1d2e36ae 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -2,8 +2,12 @@ .project-edit-errors = render 'projects/errors' .project-edit-content - %p.slead - New projects are private by default. You choose who can see the project and commit to repository. + - if Gitlab.config.gitlab.default_projects_features.public + %p.slead + New projects are public by default. Any signed in user can see your project but cannot commit to it unless granted access. + - else + %p.slead + New projects are private by default. You choose who can see the project and commit to repository. %hr = form_for @project, remote: true do |f| .control-group.project-name-holder diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index 8a3427f3980f1fa72a54c82f05259b839ea41271..06ca5169dff0e7a682eb9ea52f42f10756fde061 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -3,6 +3,7 @@ .row .span9 = render "events/event_last_push", event: @last_push + = render 'shared/event_filter' .content_list .loading.hide .span3 diff --git a/app/views/projects/team_members/_form.html.haml b/app/views/projects/team_members/_form.html.haml index 8dd2faa219b22464c23e1123ea17f583b0621d6a..5214a54e9095c7f10c1c6529840b5dc1244bea37 100644 --- a/app/views/projects/team_members/_form.html.haml +++ b/app/views/projects/team_members/_form.html.haml @@ -1,5 +1,5 @@ %h3.page-title - = "New Team member(s)" + = "New project member(s)" = form_for @user_project_relation, as: :team_member, url: project_team_members_path(@project) do |f| -if @user_project_relation.errors.any? @@ -8,16 +8,16 @@ - @user_project_relation.errors.full_messages.each do |msg| %li= msg - %h6 1. Choose people you want in the team + %p 1. Choose people you want in the project .control-group = f.label :user_ids, "People" .controls = users_select_tag(:user_ids, multiple: true) - %h6 2. Set access level for them + %p 2. Set access level for them .control-group = f.label :project_access, "Project Access" - .controls= select_tag :project_access, options_for_select(Project.access_options, @user_project_relation.project_access), class: "project-access-select chosen" + .controls= select_tag :project_access, options_for_select(Gitlab::Access.options, @user_project_relation.project_access), class: "project-access-select chosen" .form-actions = f.submit 'Add users', class: "btn btn-create" diff --git a/app/views/projects/team_members/index.html.haml b/app/views/projects/team_members/index.html.haml index 88325587f44590aeb027ca97d4cebd4bfcbb4480..acbe82919f17285d787bd4dc43d3387d6d179951 100644 --- a/app/views/projects/team_members/index.html.haml +++ b/app/views/projects/team_members/index.html.haml @@ -3,9 +3,9 @@ - if can? current_user, :admin_team_member, @project %span.pull-right - = link_to new_project_team_member_path(@project), class: "btn btn-new grouped", title: "New Team Member" do - New Team Member - = link_to import_project_team_members_path(@project), class: "btn grouped", title: "Import team from another project" do + = link_to new_project_team_member_path(@project), class: "btn btn-new grouped", title: "New project member" do + New project member + = link_to import_project_team_members_path(@project), class: "btn grouped", title: "Import members from another project" do Import members %p.light diff --git a/app/views/search/_result.html.haml b/app/views/search/_result.html.haml index fac5fdfd0b1f5f2f592a65aa02e73b5af322834d..5f7540d1b16c61bfed6f061ebfddeecd8019aa38 100644 --- a/app/views/search/_result.html.haml +++ b/app/views/search/_result.html.haml @@ -23,7 +23,7 @@ %li merge request: = link_to [merge_request.target_project, merge_request] do - %span ##{merge_request.id} + %span ##{merge_request.iid} %strong.term = truncate merge_request.title, length: 50 - if merge_request.for_fork? @@ -37,7 +37,7 @@ %li issue: = link_to [issue.project, issue] do - %span ##{issue.id} + %span ##{issue.iid} %strong.term = truncate issue.title, length: 50 %span.light (#{issue.project.name_with_namespace}) diff --git a/app/views/shared/_event_filter.html.haml b/app/views/shared/_event_filter.html.haml new file mode 100644 index 0000000000000000000000000000000000000000..ee0b57fbe5afc2f3a7d9eb71cc51e044bfdf1368 --- /dev/null +++ b/app/views/shared/_event_filter.html.haml @@ -0,0 +1,5 @@ +.event_filter + = event_filter_link EventFilter.push, 'Push events' + = event_filter_link EventFilter.merged, 'Merge events' + = event_filter_link EventFilter.comments, 'Comments' + = event_filter_link EventFilter.team, 'Team' diff --git a/app/views/shared/_filter.html.haml b/app/views/shared/_filter.html.haml index 52ca1f22d7d44570e97d810ace15b88c6d669c24..2f051cea48b7d08381979bc82dea57b419ee0352 100644 --- a/app/views/shared/_filter.html.haml +++ b/app/views/shared/_filter.html.haml @@ -12,7 +12,7 @@ All %fieldset - %legend Projects: + %legend Projects %ul.nav.nav-pills.nav-pills-small.nav-stacked - @projects.each do |project| - unless entities_per_project(project, entity).zero? diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 284098b9b1993622dc70eba192194abe9f01b599..743ab0949a1557f44064b4103fb40742dd6dc0fb 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -5,7 +5,7 @@ = @user.name - if @user == current_user .pull-right - = link_to profile_path, class: 'btn btn-small' do + = link_to profile_path, class: 'btn' do %i.icon-edit Edit Profile %br @@ -14,7 +14,7 @@ %small member since #{@user.created_at.stamp("Nov 12, 2031")} .clearfix %hr - %h5 User Activity: + %h4 User Activity: = render @events .span4 = render 'profile', user: @user diff --git a/config/application.rb b/config/application.rb index f7349fa009bb1da41e53f3083db3a9f26900d663..8ac07ef337a2065b2ad52b6b50d70ebae7723e13 100644 --- a/config/application.rb +++ b/config/application.rb @@ -32,6 +32,7 @@ module Gitlab :project_observer, :system_hook_observer, :user_observer, + :users_group_observer, :users_project_observer # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example index 59a4d1fd41b2c6264200640b1b048c160966a19b..99a23db0f1e25aa772ef617fdf2c06af08be97e9 100644 --- a/config/gitlab.yml.example +++ b/config/gitlab.yml.example @@ -41,12 +41,16 @@ production: &base ## User settings default_projects_limit: 10 # default_can_create_group: false # default: true - # default_can_create_team: false # default: true # username_changing_enabled: false # default: true - User can change her username/namespace ## Users management # signup_enabled: true # default: false - Account passwords are not sent via the email if signup is enabled. + ## Automatic issue closing + # If a commit message matches this regular express, all issues referenced from the matched text will be closed + # if it's pushed to a project's default branch. + # issue_closing_pattern: "^([Cc]loses|[Ff]ixes) +#\d+" + ## Default project features settings default_projects_features: issues: true @@ -54,6 +58,7 @@ production: &base wiki: true wall: false snippets: false + public: false ## External issues trackers issues_tracker: diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb index e0207c6f0fc96bb42a0935ea24e0dea6d01747c5..da7a83a62a4a1ba0c86111db1b748b40b32be898 100644 --- a/config/initializers/1_settings.rb +++ b/config/initializers/1_settings.rb @@ -52,7 +52,6 @@ Settings['issues_tracker'] ||= {} Settings['gitlab'] ||= Settingslogic.new({}) Settings.gitlab['default_projects_limit'] ||= 10 Settings.gitlab['default_can_create_group'] = true if Settings.gitlab['default_can_create_group'].nil? -Settings.gitlab['default_can_create_team'] = true if Settings.gitlab['default_can_create_team'].nil? Settings.gitlab['host'] ||= 'localhost' Settings.gitlab['https'] = false if Settings.gitlab['https'].nil? Settings.gitlab['port'] ||= Settings.gitlab.https ? 443 : 80 @@ -69,12 +68,14 @@ rescue ArgumentError # no user configured end Settings.gitlab['signup_enabled'] ||= false Settings.gitlab['username_changing_enabled'] = true if Settings.gitlab['username_changing_enabled'].nil? +Settings.gitlab['issue_closing_pattern'] = '^([Cc]loses|[Ff]ixes) #(\d+)' if Settings.gitlab['issue_closing_pattern'].nil? Settings.gitlab['default_projects_features'] ||= {} Settings.gitlab.default_projects_features['issues'] = true if Settings.gitlab.default_projects_features['issues'].nil? Settings.gitlab.default_projects_features['merge_requests'] = true if Settings.gitlab.default_projects_features['merge_requests'].nil? Settings.gitlab.default_projects_features['wiki'] = true if Settings.gitlab.default_projects_features['wiki'].nil? Settings.gitlab.default_projects_features['wall'] = false if Settings.gitlab.default_projects_features['wall'].nil? Settings.gitlab.default_projects_features['snippets'] = false if Settings.gitlab.default_projects_features['snippets'].nil? +Settings.gitlab.default_projects_features['public'] = false if Settings.gitlab.default_projects_features['public'].nil? # # Gravatar diff --git a/config/routes.rb b/config/routes.rb index ff84bc1527096ce48433d9e01d60b34facd497f7..d0e29a5ab96b0e0aeb7be1f0d38c57dad41b6419 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -89,11 +89,7 @@ Gitlab::Application.routes.draw do resource :logs, only: [:show] resource :background_jobs, controller: 'background_jobs', only: [:show] - - resources :projects, constraints: { id: /[a-zA-Z.\/0-9_\-]+/ }, only: [:index, :show] do - resources :members, only: [:destroy] - end - + resources :projects, constraints: { id: /[a-zA-Z.\/0-9_\-]+/ }, only: [:index, :show] root to: "dashboard#index" end @@ -281,7 +277,7 @@ Gitlab::Application.routes.draw do end end - resources :issues, except: [:destroy] do + resources :issues, constraints: {id: /\d+/}, except: [:destroy] do collection do post :bulk_update end diff --git a/config/unicorn.rb.example b/config/unicorn.rb.example index e010de6d6a963abbdb29195c1ceca13a4694b08c..812fd5b74498c785caf15177e9ea5084568fc2a7 100644 --- a/config/unicorn.rb.example +++ b/config/unicorn.rb.example @@ -26,7 +26,7 @@ working_directory "/home/git/gitlab" # available in 0.94.0+ # listen on both a Unix domain socket and a TCP port, # we use a shorter backlog for quicker failover when busy listen "/home/git/gitlab/tmp/sockets/gitlab.socket", :backlog => 64 -listen 8080, :tcp_nopush => true +listen "127.0.0.1:8080", :tcp_nopush => true # nuke workers after 30 seconds instead of 60 seconds (the default) timeout 30 @@ -64,19 +64,19 @@ before_fork do |server, worker| # installations. It is not needed if your system can house # twice as many worker_processes as you have configured. # - # # This allows a new master process to incrementally - # # phase out the old master process with SIGTTOU to avoid a - # # thundering herd (especially in the "preload_app false" case) - # # when doing a transparent upgrade. The last worker spawned - # # will then kill off the old master process with a SIGQUIT. - # old_pid = "#{server.config[:pid]}.oldbin" - # if old_pid != server.pid - # begin - # sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU - # Process.kill(sig, File.read(old_pid).to_i) - # rescue Errno::ENOENT, Errno::ESRCH - # end - # end + # This allows a new master process to incrementally + # phase out the old master process with SIGTTOU to avoid a + # thundering herd (especially in the "preload_app false" case) + # when doing a transparent upgrade. The last worker spawned + # will then kill off the old master process with a SIGQUIT. + old_pid = "#{server.config[:pid]}.oldbin" + if old_pid != server.pid + begin + sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU + Process.kill(sig, File.read(old_pid).to_i) + rescue Errno::ENOENT, Errno::ESRCH + end + end # # Throttle the master from forking too quickly by sleeping. Due # to the implementation of standard Unix signal handlers, this diff --git a/db/fixtures/development/01_admin.rb b/db/fixtures/development/01_admin.rb index 948a3459283c25bf77176efb072db7ccfedf6982..ecea8211393c86371728db405b8482f75b52984a 100644 --- a/db/fixtures/development/01_admin.rb +++ b/db/fixtures/development/01_admin.rb @@ -8,5 +8,6 @@ User.seed(:id, [ password_confirmation: "5iveL!fe", admin: true, projects_limit: 100, + theme_id: Gitlab::Theme::MARS } ]) diff --git a/db/fixtures/development/07_milestones.rb b/db/fixtures/development/07_milestones.rb index 3135bf3a4f4293e713b77e9fbfd2bc4f24079713..6fe7e2467701bbf309d8be1e785f689416cc1381 100644 --- a/db/fixtures/development/07_milestones.rb +++ b/db/fixtures/development/07_milestones.rb @@ -11,3 +11,8 @@ Milestone.seed(:id, [ { id: 9, project_id: 3, title: 'v' + Faker::Address.zip_code }, { id: 11, project_id: 3, title: 'v' + Faker::Address.zip_code }, ]) + +Milestone.all.map do |ml| + ml.set_iid + ml.save +end diff --git a/db/fixtures/development/09_issues.rb b/db/fixtures/development/09_issues.rb index 627579721d0fd656883fa17cc4452f795580ec3b..31ba77254a399ca08f841c73d0c987d855bef99e 100644 --- a/db/fixtures/development/09_issues.rb +++ b/db/fixtures/development/09_issues.rb @@ -24,4 +24,9 @@ Gitlab::Seeder.quiet do }]) print('.') end + + Issue.all.map do |issue| + issue.set_iid + issue.save + end end diff --git a/db/fixtures/development/10_merge_requests.rb b/db/fixtures/development/10_merge_requests.rb index bf247adb41651f3ffe2bf77a75ee3d03e9f50f9e..1e61ea28636e7b44df374ad62e2516c23d63e0af 100644 --- a/db/fixtures/development/10_merge_requests.rb +++ b/db/fixtures/development/10_merge_requests.rb @@ -34,6 +34,11 @@ Gitlab::Seeder.quiet do end end +MergeRequest.all.map do |mr| + mr.set_iid + mr.save +end + puts 'Load diffs for Merge Requests (it will take some time)...' MergeRequest.all.each do |mr| mr.reload_code diff --git a/db/fixtures/production/001_admin.rb b/db/fixtures/production/001_admin.rb index 632f6107b33f4a2edee91b604b68c3bfd07be243..1b77d94905d7f6dad2ba7e4c277074fdb7577705 100644 --- a/db/fixtures/production/001_admin.rb +++ b/db/fixtures/production/001_admin.rb @@ -4,7 +4,9 @@ admin = User.create( username: 'root', password: "5iveL!fe", password_confirmation: "5iveL!fe", - password_expires_at: Time.now + password_expires_at: Time.now, + theme_id: Gitlab::Theme::MARS + ) admin.projects_limit = 10000 diff --git a/db/migrate/20130419190306_allow_merges_for_forks.rb b/db/migrate/20130419190306_allow_merges_for_forks.rb index 691293a1c3ec31e05688ff5970609b8bccd566d8..56ce58a846dba43d724635aaaeb7b16d7a0ee2a4 100644 --- a/db/migrate/20130419190306_allow_merges_for_forks.rb +++ b/db/migrate/20130419190306_allow_merges_for_forks.rb @@ -1,7 +1,8 @@ class AllowMergesForForks < ActiveRecord::Migration def self.up - add_column :merge_requests, :target_project_id, :integer, :null => false + add_column :merge_requests, :target_project_id, :integer, :null => true MergeRequest.update_all("target_project_id = project_id") + change_column :merge_requests, :target_project_id, :integer, :null => false rename_column :merge_requests, :project_id, :source_project_id end diff --git a/db/migrate/20130528184641_add_system_to_notes.rb b/db/migrate/20130528184641_add_system_to_notes.rb new file mode 100644 index 0000000000000000000000000000000000000000..1b22a4934f992d8c4ae109456a1ab4f6d014a12b --- /dev/null +++ b/db/migrate/20130528184641_add_system_to_notes.rb @@ -0,0 +1,16 @@ +class AddSystemToNotes < ActiveRecord::Migration + class Note < ActiveRecord::Base + end + + def up + add_column :notes, :system, :boolean, default: false, null: false + + Note.reset_column_information + Note.update_all(system: false) + Note.where("note like '_status changed to%'").update_all(system: true) + end + + def down + remove_column :notes, :system + end +end diff --git a/db/migrate/20130819182730_add_internal_ids_to_issues_and_mr.rb b/db/migrate/20130819182730_add_internal_ids_to_issues_and_mr.rb new file mode 100644 index 0000000000000000000000000000000000000000..e55ae38f144d5f4f181fa5a9b287834ec73f00fb --- /dev/null +++ b/db/migrate/20130819182730_add_internal_ids_to_issues_and_mr.rb @@ -0,0 +1,6 @@ +class AddInternalIdsToIssuesAndMr < ActiveRecord::Migration + def change + add_column :issues, :iid, :integer + add_column :merge_requests, :iid, :integer + end +end diff --git a/db/migrate/20130821090530_remove_deprecated_tables.rb b/db/migrate/20130821090530_remove_deprecated_tables.rb new file mode 100644 index 0000000000000000000000000000000000000000..539c0617eeb7747e851da95d6ba6b5b96934c73f --- /dev/null +++ b/db/migrate/20130821090530_remove_deprecated_tables.rb @@ -0,0 +1,11 @@ +class RemoveDeprecatedTables < ActiveRecord::Migration + def up + drop_table :user_teams + drop_table :user_team_project_relationships + drop_table :user_team_user_relationships + end + + def down + raise 'No rollback for this migration' + end +end diff --git a/db/migrate/20130821090531_add_internal_ids_to_milestones.rb b/db/migrate/20130821090531_add_internal_ids_to_milestones.rb new file mode 100644 index 0000000000000000000000000000000000000000..33e5bae580552a984fbb84e91ef94b1a3f4003f3 --- /dev/null +++ b/db/migrate/20130821090531_add_internal_ids_to_milestones.rb @@ -0,0 +1,5 @@ +class AddInternalIdsToMilestones < ActiveRecord::Migration + def change + add_column :milestones, :iid, :integer + end +end diff --git a/db/migrate/20130909132950_add_description_to_merge_request.rb b/db/migrate/20130909132950_add_description_to_merge_request.rb new file mode 100644 index 0000000000000000000000000000000000000000..9bcd0c7ee0689122e979b5ea63f7b04a0348f768 --- /dev/null +++ b/db/migrate/20130909132950_add_description_to_merge_request.rb @@ -0,0 +1,5 @@ +class AddDescriptionToMergeRequest < ActiveRecord::Migration + def change + add_column :merge_requests, :description, :text, null: true + end +end diff --git a/db/schema.rb b/db/schema.rb index e0e7d47b92ecbefb0069aefb2f5442d7a2834aa4..d5effe40ea1f0e9ec0663fbc2fc1797a2563e30d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended to check this file into your version control system. -ActiveRecord::Schema.define(:version => 20130812143708) do +ActiveRecord::Schema.define(:version => 20130909132950) do create_table "deploy_keys_projects", :force => true do |t| t.integer "deploy_key_id", :null => false @@ -62,6 +62,7 @@ ActiveRecord::Schema.define(:version => 20130812143708) do t.text "description" t.integer "milestone_id" t.string "state" + t.integer "iid" end add_index "issues", ["assignee_id"], :name => "index_issues_on_assignee_id" @@ -98,6 +99,8 @@ ActiveRecord::Schema.define(:version => 20130812143708) do t.string "state" t.string "merge_status" t.integer "target_project_id", :null => false + t.integer "iid" + t.text "description" end add_index "merge_requests", ["assignee_id"], :name => "index_merge_requests_on_assignee_id" @@ -117,6 +120,7 @@ ActiveRecord::Schema.define(:version => 20130812143708) do t.datetime "created_at", :null => false t.datetime "updated_at", :null => false t.string "state" + t.integer "iid" end add_index "milestones", ["due_date"], :name => "index_milestones_on_due_date" @@ -141,14 +145,15 @@ ActiveRecord::Schema.define(:version => 20130812143708) do t.text "note" t.string "noteable_type" t.integer "author_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false + t.datetime "created_at", :null => false + t.datetime "updated_at", :null => false t.integer "project_id" t.string "attachment" t.string "line_code" t.string "commit_id" t.integer "noteable_id" t.text "st_diff" + t.boolean "system", :default => false, :null => false end add_index "notes", ["author_id"], :name => "index_notes_on_author_id" @@ -244,32 +249,6 @@ ActiveRecord::Schema.define(:version => 20130812143708) do t.string "name" end - create_table "user_team_project_relationships", :force => true do |t| - t.integer "project_id" - t.integer "user_team_id" - t.integer "greatest_access" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "user_team_user_relationships", :force => true do |t| - t.integer "user_id" - t.integer "user_team_id" - t.boolean "group_admin" - t.integer "permission" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - end - - create_table "user_teams", :force => true do |t| - t.string "name" - t.string "path" - t.integer "owner_id" - t.datetime "created_at", :null => false - t.datetime "updated_at", :null => false - t.string "description", :default => "", :null => false - end - create_table "users", :force => true do |t| t.string "email", :default => "", :null => false t.string "encrypted_password", :default => "", :null => false diff --git a/doc/api/README.md b/doc/api/README.md index 8e4eb296b4039df5a500dbbffe1c3b239b348984..6971e08f010418207bb2bff312365466e51b656e 100644 --- a/doc/api/README.md +++ b/doc/api/README.md @@ -58,7 +58,43 @@ Return values: * `409 Conflict` - A conflicting resource already exists, e.g. creating a project with a name that already exists * `500 Server Error` - While handling the request something went wrong on the server side +## Sudo +All API requests support performing an api call as if you were another user, if your private token is for an administration account. You need to pass `sudo` parameter by url or header with an id or username of the user you want to perform the operation as. If passed as header, the header name must be "SUDO" (capitals). +If a non administrative `private_token` is provided then an error message will be returned with status code 403: + +```json +{ + "message": "403 Forbidden: Must be admin to use sudo" +} +``` + +If the sudo user id or username cannot be found then an error message will be returned with status code 404: + +```json +{ + "message": "404 Not Found: No user id or username for: