diff options
193 files changed, 2580 insertions, 1604 deletions
diff --git a/.gitignore b/.gitignore index 2c6b65b7b7d..7a7b5c93936 100644 --- a/.gitignore +++ b/.gitignore @@ -1,42 +1,42 @@ +*.log +*.swp +.DS_Store .bundle +.chef +.directory +.envrc +.gitlab_shell_secret +.idea +.rbenv-version .rbx/ -db/*.sqlite3 -db/*.sqlite3-journal -log/*.log* -tmp/ -.sass-cache/ -coverage/* -backups/* -*.swp -public/uploads/ -.ruby-version .ruby-gemset +.ruby-version .rvmrc -.rbenv-version -.directory -nohup.out -Vagrantfile +.sass-cache/ +.secret .vagrant -config/gitlab.yml +Vagrantfile +backups/* +config/aws.yml config/database.yml +config/gitlab.yml config/initializers/omniauth.rb config/initializers/rack_attack.rb config/initializers/smtp_settings.rb -config/unicorn.rb config/resque.yml -config/aws.yml +config/unicorn.rb +coverage/* +db/*.sqlite3 +db/*.sqlite3-journal db/data.yml -.idea -.DS_Store -.chef -vendor/bundle/* -rails_best_practices_output.html doc/code/* -.secret -*.log -public/uploads.* -public/assets/ -.envrc dump.rdb +log/*.log* +nohup.out +public/assets/ +public/uploads.* +public/uploads/ +rails_best_practices_output.html tags -.gitlab_shell_secret +tmp/ +vendor/bundle/* diff --git a/CHANGELOG b/CHANGELOG index 2061237fb42..e5e1c7d349b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,20 +1,33 @@ +v 7.7.0 + - + - + - + - + - + - + - + - + - OAuth applications feature + - + - + - Set project path instead of project name in create form + - + - + - New side navigation + + + v 7.6.0 - Fork repository to groups - New rugged version - Add CRON=1 backup setting for quiet backups - Fix failing wiki restore - - - Add optional Sidekiq MemoryKiller middleware (enabled via SIDEKIQ_MAX_RSS env variable) - - - - - Monokai highlighting style now more faithful to original design (Mark Riedesel) - Create project with repository in synchrony - Added ability to create empty repo or import existing one if project does not have repository - - - - - Reactivate highlight.js language autodetection - Mobile UI improvements - - - Change maximum avatar file size from 100KB to 200KB - Strict validation for snippet file names - Enable Markdown preview for issues, merge requests, milestones, and notes (Vinnie Okada) @@ -22,6 +35,11 @@ v 7.6.0 - Update Sidekiq to version 2.17.8 - Add author filter to project issues and merge requests pages - Atom feed for user activity + - Support multiple omniauth providers for the same user + - Rendering cross reference in issue title and tooltip for merge request + - Show username in comments + - Possibility to create Milestones or Labels when Issues are disabled + - Fix bug with showing gpg signature in tag v 7.5.2 - Don't log Sidekiq arguments by default diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2195ea6e739..c82a4c623e0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -101,6 +101,16 @@ Please ensure you support the feature you contribute through all of these steps. 1. Community questions answered 1. Answers to questions radiated (in docs/wiki/etc.) +If you add a dependency in GitLab (such as an operating system package) please consider updating the following and note the applicability of each in your merge request: + +1. Note the addition in the release blog post (create one if it doesn't exist yet) https://gitlab.com/gitlab-com/www-gitlab-com/merge_requests/ +1. Upgrade guide, for example https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/update/7.5-to-7.6.md +1. Upgrader https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/update/upgrader.md#2-run-gitlab-upgrade-tool +1. Installation guide https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md#1-packages-dependencies +1. GitLab Development Kit https://gitlab.com/gitlab-org/gitlab-development-kit +1. Test suite https://gitlab.com/gitlab-org/gitlab-ci/blob/master/doc/examples/configure_a_runner_to_run_the_gitlab_ce_test_suite.md +1. Omnibus package creator https://gitlab.com/gitlab-org/omnibus-gitlab + ## Merge request description format 1. What does this MR do? @@ -118,6 +128,7 @@ Please ensure you support the feature you contribute through all of these steps. 1. Can merge without problems (if not please merge `master`, never rebase commits pushed to the remote server) 1. Does not break any existing functionality 1. Fixes one specific issue or implements one specific feature (do not combine things, send separate merge requests if needed) +1. Migrations should do only one thing (eg: either create a table, move data to a new table or remove an old table) to aid retrying on failure 1. Keeps the GitLab code base clean and well structured 1. Contains functionality we think other users will benefit from too 1. Doesn't add configuration options since they complicate future changes @@ -142,3 +153,17 @@ Please ensure you support the feature you contribute through all of these steps. 1. [Markdown](http://www.cirosantilli.com/markdown-styleguide) This is also the style used by linting tools such as [RuboCop](https://github.com/bbatsov/rubocop), [PullReview](https://www.pullreview.com/) and [Hound CI](https://houndci.com). + +## Code of conduct +As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. + +We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion. + +Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team. + +Instances of abusive, harassing, or otherwise unacceptable behavior can be +reported by emailing contact@gitlab.com + +This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/) @@ -28,6 +28,9 @@ gem 'omniauth-google-oauth2' gem 'omniauth-twitter' gem 'omniauth-github' gem 'omniauth-shibboleth' +gem 'omniauth-kerberos' +gem 'doorkeeper', '2.0.1' +gem "rack-oauth2", "~> 1.0.5" # Extracting information from a git repository # Provide access to Gitlab::Git library diff --git a/Gemfile.lock b/Gemfile.lock index 4bcb1eb0de5..0d089305fe5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -37,6 +37,7 @@ GEM rake (>= 0.8.7) arel (5.0.1.20140414130214) asciidoctor (0.1.4) + attr_required (1.0.0) awesome_print (1.2.0) axiom-types (0.0.5) descendants_tracker (~> 0.0.1) @@ -107,6 +108,8 @@ GEM diff-lcs (1.2.5) diffy (3.0.3) docile (1.1.5) + doorkeeper (2.0.1) + railties (>= 3.1) dotenv (0.9.0) dropzonejs-rails (0.4.14) rails (> 3.1) @@ -158,7 +161,7 @@ GEM dotenv (>= 0.7) thor (>= 0.13.6) formatador (0.2.4) - gemnasium-gitlab-service (0.2.2) + gemnasium-gitlab-service (0.2.3) rugged (~> 0.19) gherkin-ruby (0.3.1) racc @@ -250,6 +253,7 @@ GEM json (~> 1.8) multi_xml (>= 0.5.2) httpauth (0.2.1) + httpclient (2.5.3.3) i18n (0.6.11) ice_nine (0.10.0) jasmine (2.0.2) @@ -322,6 +326,11 @@ GEM omniauth-google-oauth2 (0.2.5) omniauth (> 1.0) omniauth-oauth2 (~> 1.1) + omniauth-kerberos (0.2.0) + omniauth-multipassword + timfel-krb5-auth (~> 0.8) + omniauth-multipassword (0.4.1) + omniauth (~> 1.0) omniauth-oauth (1.0.1) oauth omniauth (~> 1.0) @@ -363,6 +372,12 @@ GEM rack (>= 1.1.3) rack-mount (0.8.3) rack (>= 1.0.0) + rack-oauth2 (1.0.8) + activesupport (>= 2.3) + attr_required (>= 0.0.5) + httpclient (>= 2.2.0.2) + multi_json (>= 1.3.6) + rack (>= 1.1) rack-protection (1.5.1) rack rack-test (0.6.2) @@ -531,6 +546,7 @@ GEM thread_safe (0.3.4) tilt (1.4.1) timers (1.1.0) + timfel-krb5-auth (0.8) tinder (1.9.3) eventmachine (~> 1.0) faraday (~> 0.8) @@ -610,6 +626,7 @@ DEPENDENCIES devise (= 3.2.4) devise-async (= 0.9.0) diffy (~> 3.0.3) + doorkeeper (= 2.0.1) dropzonejs-rails email_spec enumerize @@ -655,6 +672,7 @@ DEPENDENCIES omniauth (~> 1.1.3) omniauth-github omniauth-google-oauth2 + omniauth-kerberos omniauth-shibboleth omniauth-twitter org-ruby (= 0.9.9) @@ -665,6 +683,7 @@ DEPENDENCIES rack-attack rack-cors rack-mini-profiler + rack-oauth2 (~> 1.0.5) rails (~> 4.1.0) rails_autolink (~> 1.1) rails_best_practices diff --git a/PROCESS.md b/PROCESS.md index 1dd28d6b670..5cc25de05a4 100644 --- a/PROCESS.md +++ b/PROCESS.md @@ -104,3 +104,10 @@ This merge request has been closed because a request for more information has no ### Accepting merge requests Is there a request on [the feature request forum](http://feedback.gitlab.com/forums/176466-general) that is similar to this? If so, can you make a comment with a link to it? Please be aware that new functionality that is not marked [accepting merge/pull requests](http://feedback.gitlab.com/forums/176466-general/status/796455) on the forum might not make it into GitLab. You might be asked to make changes and even after implementing them your feature might still be declined. If you want to reduce the chance of this happening please have a discussion in the forum first. + +### Only accepting merge requests with green tests + +We can only accept a merge request if all the tests are green. I've just +restarted the build. When the tests are still not passing after this restart and +you're sure that is does not have anything to do with your code changes, please +rebase with master to see if that solves the issue. diff --git a/README.md b/README.md index f303e8e7383..afcaaf0f0fa 100644 --- a/README.md +++ b/README.md @@ -52,69 +52,30 @@ On [about.gitlab.com](https://about.gitlab.com/) you can find more information a Please see [the installation page on the GitLab website](https://about.gitlab.com/installation/) for the various options. Since a manual installation is a lot of work and error prone we strongly recommend the fast and reliable [Omnibus package installation](https://about.gitlab.com/downloads/) (deb/rpm). +You can access new installation with the login `root` and password `5iveL!fe`, after login you are required to set a unique password. ## Third-party applications There are a lot of applications and API wrappers for GitLab. Find them [on our website](https://about.gitlab.com/applications/). -### New versions +## New versions Since 2011 a minor or major version of GitLab is released on the 22nd of every month. Patch and security releases come out when needed. New features are detailed on the [blog](https://about.gitlab.com/blog/) and in the [changelog](CHANGELOG). For more information about the release process see the release [documentation](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/release). Features that will likely be in the next releases can be found on the [feature request 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). -### Upgrading +## Upgrading For updating the the Omnibus installation please see the [update documentation](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/update.md). For manual installations there is an [upgrader script](doc/update/upgrader.md) and there are [upgrade guides](doc/update). -## Run in production mode - -The Installation guide contains instructions on how to download an init script and run it automatically on boot. You can also start the init script manually: - - sudo service gitlab start - -or by directly calling the script: - - sudo /etc/init.d/gitlab start - -Please login with `root` / `5iveL!fe` - ## Install a development environment We recommend setting up your development environment with [the GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit). -If you do not use the development kit you might need to copy the example development unicorn configuration file +If you do not use the GitLab Development Development kit you need to install and setup all the dependencies yourself, this is a lot of work and error prone. +One small thing you also have to do when installing it yourself is to copy the example development unicorn configuration file: cp config/unicorn.rb.example.development config/unicorn.rb -## Run in development mode - -Start it with [Foreman](https://github.com/ddollar/foreman) - - bundle exec foreman start -p 3000 - -or start each component separately: - - bundle exec rails s - bin/background_jobs start - -And surf to [localhost:3000](http://localhost:3000/) and login with `root` / `5iveL!fe`. - -## Run the tests - -- Run all tests: - - bundle exec rake test - -- [RSpec](http://rspec.info/) unit and functional tests. - - All RSpec tests: `bundle exec rake spec` - - Single RSpec file: `bundle exec rspec spec/controllers/commit_controller_spec.rb` - -- [Spinach](https://github.com/codegram/spinach) integration tests. - - All Spinach tests: `bundle exec rake spinach` - - Single Spinach test: `bundle exec spinach features/project/issues/milestones.feature` +Instructions on how to start Gitlab and how to run the tests can be found in the [development section of the GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit#development). ## Documentation diff --git a/app/assets/javascripts/activities.js.coffee b/app/assets/javascripts/activities.js.coffee index 4f76d8ce486..777c62dc1b7 100644 --- a/app/assets/javascripts/activities.js.coffee +++ b/app/assets/javascripts/activities.js.coffee @@ -12,7 +12,7 @@ class @Activities toggleFilter: (sender) -> - sender.parent().toggleClass "inactive" + sender.parent().toggleClass "active" event_filters = $.cookie("event_filter") filter = sender.attr("id").split("_")[0] if event_filters diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index e9a28c12159..4cda8b75d8e 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -51,12 +51,6 @@ window.ajaxGet = (url) -> window.showAndHide = (selector) -> -window.errorMessage = (message) -> - ehtml = $("<p>") - ehtml.addClass("error_message") - ehtml.html(message) - ehtml - window.split = (val) -> return val.split( /,\s*/ ) diff --git a/app/assets/javascripts/issue.js.coffee b/app/assets/javascripts/issue.js.coffee index 597b4695a6d..45c248e6fb6 100644 --- a/app/assets/javascripts/issue.js.coffee +++ b/app/assets/javascripts/issue.js.coffee @@ -1,9 +1,9 @@ class @Issue constructor: -> $('.edit-issue.inline-update input[type="submit"]').hide() - $(".issue-box .inline-update").on "change", "select", -> + $(".context .inline-update").on "change", "select", -> $(this).submit() - $(".issue-box .inline-update").on "change", "#issue_assignee_id", -> + $(".context .inline-update").on "change", "#issue_assignee_id", -> $(this).submit() if $("a.btn-close").length diff --git a/app/assets/javascripts/merge_request.js.coffee b/app/assets/javascripts/merge_request.js.coffee index 46e06424e5a..9e3ca45ce04 100644 --- a/app/assets/javascripts/merge_request.js.coffee +++ b/app/assets/javascripts/merge_request.js.coffee @@ -26,9 +26,9 @@ class @MergeRequest initContextWidget: -> $('.edit-merge_request.inline-update input[type="submit"]').hide() - $(".issue-box .inline-update").on "change", "select", -> + $(".context .inline-update").on "change", "select", -> $(this).submit() - $(".issue-box .inline-update").on "change", "#merge_request_assignee_id", -> + $(".context .inline-update").on "change", "#merge_request_assignee_id", -> $(this).submit() initMergeWidget: -> @@ -89,6 +89,9 @@ class @MergeRequest this.$('.merge-request-tabs .diffs-tab').addClass 'active' this.loadDiff() unless @diffs_loaded this.$('.diffs').show() + when 'commits' + this.$('.merge-request-tabs .commits-tab').addClass 'active' + this.$('.commits').show() else this.$('.merge-request-tabs .notes-tab').addClass 'active' this.$('.notes').show() diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index 30f8530dfda..4d1c81d91d4 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -375,7 +375,7 @@ class @Notes ### addDiffNote: (e) => e.preventDefault() - link = e.target + link = e.currentTarget form = $(".js-new-note-form") row = $(link).closest("tr") nextRow = row.next() diff --git a/app/assets/javascripts/stat_graph_contributors_graph.js.coffee b/app/assets/javascripts/stat_graph_contributors_graph.js.coffee index 9952fa0b00a..8b82d20c6c2 100644 --- a/app/assets/javascripts/stat_graph_contributors_graph.js.coffee +++ b/app/assets/javascripts/stat_graph_contributors_graph.js.coffee @@ -46,7 +46,7 @@ class @ContributorsGraph class @ContributorsMasterGraph extends ContributorsGraph constructor: (@data) -> - @width = $('.container').width() - 70 + @width = $('.container').width() - 345 @height = 200 @x = null @y = null @@ -119,7 +119,7 @@ class @ContributorsMasterGraph extends ContributorsGraph class @ContributorsAuthorGraph extends ContributorsGraph constructor: (@data) -> - @width = $('.container').width()/2 - 100 + @width = $('.container').width()/2 - 225 @height = 200 @x = null @y = null diff --git a/app/assets/stylesheets/generic/common.scss b/app/assets/stylesheets/generic/common.scss index 2fc738c18d8..19c8f103535 100644 --- a/app/assets/stylesheets/generic/common.scss +++ b/app/assets/stylesheets/generic/common.scss @@ -207,26 +207,6 @@ li.note { } } -.no-ssh-key-message { - padding: 10px 0; - background: #C67; - margin: 0; - color: #FFF; - margin-top: -1px; - text-align: center; - - a { - color: #fff; - text-decoration: underline; - } - - .links-xs { - text-align: center; - font-size: 16px; - padding: 5px; - } -} - .warning_message { border-left: 4px solid #ed9; color: #b90; @@ -282,7 +262,7 @@ img.emoji { } .navless-container { - margin-top: 20px; + margin-top: 68px; } .description-block { @@ -355,3 +335,9 @@ table { .task-status { margin-left: 10px; } + +#nprogress .spinner { + top: auto !important; + bottom: 20px !important; + left: 20px !important; +} diff --git a/app/assets/stylesheets/generic/issue_box.scss b/app/assets/stylesheets/generic/issue_box.scss index 79fbad4b946..2563ab516e2 100644 --- a/app/assets/stylesheets/generic/issue_box.scss +++ b/app/assets/stylesheets/generic/issue_box.scss @@ -1,128 +1,32 @@ /** - * Issue box: - * Huge block (one per page) for storing title, descripion and other information. + * Issue box for showing Open/Closed state: * Used for Issue#show page, MergeRequest#show page etc * - * CLasses: - * .issue-box - Regular box */ .issue-box { - color: #555; - margin:20px 0; - background: $box_bg; - @include box-shadow(0 1px 1px rgba(0, 0, 0, 0.09)); + display: inline-block; + padding: 7px 13px; + font-weight: normal; + margin-right: 5px; &.issue-box-closed { - .state { - background-color: #F3CECE; - border-color: $border_danger; - } - .state-label { - background-color: $bg_danger; - color: #FFF; - } + background-color: $bg_danger; + color: #FFF; } &.issue-box-merged { - .state { - background-color: #B7CEE7; - border-color: $border_primary; - } - .state-label { - background-color: $bg_primary; - color: #FFF; - } + background-color: $bg_primary; + color: #FFF; } &.issue-box-open { - .state { - background-color: #D6F1D7; - border-color: $bg_success; - } - .state-label { - background-color: $bg_success; - color: #FFF; - } + background-color: $bg_success; + color: #FFF; } &.issue-box-expired { - .state { - background-color: #EEE9B3; - border-color: #faebcc; - } - .state-label { - background: #cea61b; - color: #FFF; - } - } - - .control-group { - margin-bottom: 0; - } - - .state { - background-color: #f9f9f9; - } - - .title { - font-size: 28px; - font-weight: normal; - line-height: 1.5; - margin: 0; - color: #333; - padding: 10px 15px; - } - - .context { - border: none; - border-top: 1px solid #eee; - padding: 10px 15px; - - // Reset text align for children - .text-right > * { text-align: left; } - - @media (max-width: $screen-xs-max) { - // Don't right align on mobile - .text-right { text-align: left; } - - .row .col-md-6 { - padding-top: 5px; - } - } - } - - .description { - padding: 0 15px 10px 15px; - - code { - white-space: pre-wrap; - } - } - - .title, .context, .description { - .clearfix { - margin: 0; - } - } - - .state-label { - font-size: 14px; - float: left; - font-weight: bold; - padding: 10px 15px; - } - - .cross-project-ref { - float: left; - padding: 10px 15px; - } - - .creator { - float: right; - padding: 10px 15px; - a { - text-decoration: underline; - } + background: #cea61b; + color: #FFF; } } diff --git a/app/assets/stylesheets/generic/tables.scss b/app/assets/stylesheets/generic/tables.scss new file mode 100644 index 00000000000..71a7d4abaee --- /dev/null +++ b/app/assets/stylesheets/generic/tables.scss @@ -0,0 +1,20 @@ +table { + &.table { + tr { + td, th { + padding: 8px 10px; + line-height: 20px; + vertical-align: middle; + } + th { + font-weight: normal; + font-size: 15px; + border-bottom: 1px solid #CCC !important; + } + td { + border-color: #F1F1F1 !important; + border-bottom: 1px solid; + } + } + } +} diff --git a/app/assets/stylesheets/generic/timeline.scss b/app/assets/stylesheets/generic/timeline.scss index 57e9e8ae5c5..82ee41b71bd 100644 --- a/app/assets/stylesheets/generic/timeline.scss +++ b/app/assets/stylesheets/generic/timeline.scss @@ -74,6 +74,42 @@ } } } + + .system-note .timeline-entry-inner { + .timeline-icon { + background: none; + margin-left: 12px; + margin-top: 0; + @include box-shadow(none); + + span { + margin: 0 2px; + font-size: 16px; + color: #eeeeee; + } + } + + .timeline-content { + background: none; + margin-left: 45px; + padding: 0px 15px; + + &:after { border: 0; } + + .note-header { + span { font-size: 12px; } + + .avatar { + margin-right: 5px; + } + } + + .note-text { + font-size: 12px; + margin-left: 20px; + } + } + } } @media (max-width: $screen-xs-max) { diff --git a/app/assets/stylesheets/gl_bootstrap.scss b/app/assets/stylesheets/gl_bootstrap.scss index 9c5e76ab8e2..2a68d922bb7 100644 --- a/app/assets/stylesheets/gl_bootstrap.scss +++ b/app/assets/stylesheets/gl_bootstrap.scss @@ -148,6 +148,10 @@ $list-group-active-bg: $bg_primary; color: #666; } +.nav-compact > li > a { + padding: 6px 12px; +} + .nav-small > li > a { padding: 3px 5px; font-size: 12px; diff --git a/app/assets/stylesheets/main/layout.scss b/app/assets/stylesheets/main/layout.scss index 2800feb81f2..71522443f10 100644 --- a/app/assets/stylesheets/main/layout.scss +++ b/app/assets/stylesheets/main/layout.scss @@ -4,10 +4,6 @@ html { &.touch .tooltip { display: none !important; } } -body { - padding-bottom: 20px; -} - .container { padding-top: 0; z-index: 5; diff --git a/app/assets/stylesheets/main/variables.scss b/app/assets/stylesheets/main/variables.scss index c71984a5665..92b220f8019 100644 --- a/app/assets/stylesheets/main/variables.scss +++ b/app/assets/stylesheets/main/variables.scss @@ -44,6 +44,6 @@ $added: #63c363; $deleted: #f77; /** - * + * NProgress customize */ -$nprogress-color: #3498db; +$nprogress-color: #c0392b; diff --git a/app/assets/stylesheets/sections/dashboard.scss b/app/assets/stylesheets/sections/dashboard.scss index d181d83e857..e540f7ff940 100644 --- a/app/assets/stylesheets/sections/dashboard.scss +++ b/app/assets/stylesheets/sections/dashboard.scss @@ -23,20 +23,6 @@ } } -.dashboard { - .dash-filter { - width: 205px; - float: left; - height: inherit; - } -} - -@media (max-width: 1200px) { - .dashboard .dash-filter { - width: 140px; - } -} - .dash-sidebar-tabs { margin-bottom: 2px; border: none; diff --git a/app/assets/stylesheets/sections/events.scss b/app/assets/stylesheets/sections/events.scss index a766d6e77ab..93ad17f57c0 100644 --- a/app/assets/stylesheets/sections/events.scss +++ b/app/assets/stylesheets/sections/events.scss @@ -140,36 +140,6 @@ } } -/** - * Event filter - * - */ -.event_filter { - position: absolute; - width: 40px; - margin-left: -55px; - - .filter_icon { - a { - text-align:center; - background: $bg_primary; - margin-bottom: 10px; - float: left; - padding: 9px 6px; - font-size: 18px; - width: 40px; - color: #FFF; - @include border-radius(3px); - } - - &.inactive { - a { - color: #DDD; - background: #f9f9f9; - } - } - } -} /* * Last push widget @@ -203,3 +173,7 @@ } } } + +.event_filter li a { + padding: 5px 10px; +} diff --git a/app/assets/stylesheets/sections/header.scss b/app/assets/stylesheets/sections/header.scss index 9ad1a1db2cd..32b0b10c649 100644 --- a/app/assets/stylesheets/sections/header.scss +++ b/app/assets/stylesheets/sections/header.scss @@ -4,9 +4,13 @@ */ header { &.navbar-gitlab { + z-index: 100; margin-bottom: 0; min-height: 40px; border: none; + position: fixed; + top: 0; + width: 100%; .navbar-inner { filter: none; @@ -52,8 +56,6 @@ header { border-width: 0; font-size: 18px; - .app_logo { margin-left: -15px; } - .title { @include str-truncated(70%); } @@ -84,7 +86,10 @@ header { } } - z-index: 10; + .container { + width: 100% !important; + padding-left: 0px; + } /** * @@ -232,21 +237,6 @@ header { color: #fff; } } - - .app_logo { - .separator { - margin-left: 0; - margin-right: 0; - } - } - - .separator { - float: left; - height: 46px; - width: 2px; - margin-left: 10px; - margin-right: 10px; - } } .search .search-input { diff --git a/app/assets/stylesheets/sections/issues.scss b/app/assets/stylesheets/sections/issues.scss index 9a5400fffbc..929838379cb 100644 --- a/app/assets/stylesheets/sections/issues.scss +++ b/app/assets/stylesheets/sections/issues.scss @@ -162,3 +162,7 @@ form.edit-issue { } } } + +.issue-title { + margin-top: 0; +} diff --git a/app/assets/stylesheets/sections/merge_requests.scss b/app/assets/stylesheets/sections/merge_requests.scss index ec844cc00b0..920702ff3c4 100644 --- a/app/assets/stylesheets/sections/merge_requests.scss +++ b/app/assets/stylesheets/sections/merge_requests.scss @@ -19,17 +19,15 @@ } } -.merge-request .merge-request-tabs{ - border-bottom: 2px solid $border_primary; - margin: 20px 0; - - li { - a { - padding: 15px 40px; - font-size: 14px; - margin-bottom: -2px; - border-bottom: 2px solid $border_primary; - @include border-radius(0px); +@media(min-width: $screen-sm-max) { + .merge-request .merge-request-tabs{ + margin: 20px 0; + + li { + a { + padding: 15px 40px; + font-size: 14px; + } } } } @@ -106,6 +104,7 @@ .mr-state-widget { background: $box_bg; margin-bottom: 20px; + color: #666; @include box-shadow(0 1px 1px rgba(0, 0, 0, 0.09)); .ci_widget { @@ -150,7 +149,6 @@ padding: 10px 15px; h4 { - font-size: 20px; font-weight: normal; } diff --git a/app/assets/stylesheets/sections/nav.scss b/app/assets/stylesheets/sections/nav.scss deleted file mode 100644 index ccd672c5f67..00000000000 --- a/app/assets/stylesheets/sections/nav.scss +++ /dev/null @@ -1,96 +0,0 @@ -.main-nav { - background: #f5f5f5; - margin: 20px 0; - margin-top: 0; - padding-top: 4px; - border-bottom: 1px solid #E9E9E9; - - ul { - padding: 0; - margin: auto; - .count { - font-weight: normal; - display: inline-block; - height: 15px; - padding: 1px 6px; - height: auto; - font-size: 0.82em; - line-height: 14px; - text-align: center; - color: #777; - background: #eee; - @include border-radius(8px); - } - .label { - background: $hover; - text-shadow: none; - color: $style_color; - } - li { - list-style-type: none; - margin: 0; - display: table-cell; - width: 1%; - &.active { - a { - color: $link_color; - font-weight: bold; - border-bottom: 3px solid $link_color; - } - } - - &:hover { - a { - color: $link_hover_color; - border-bottom: 3px solid $link_hover_color; - } - } - } - a { - display: block; - text-align: center; - font-weight: bold; - height: 42px; - line-height: 39px; - color: #777; - text-shadow: 0 1px 1px white; - text-decoration: none; - overflow: hidden; - margin-bottom: -1px; - } - } - - @media (max-width: $screen-xs-max) { - font-size: 18px; - margin: 0; - max-height: none; - - &, .container { - padding: 0; - border-top: 0; - } - - ul { - height: auto; - - li { - display: list-item; - width: auto; - padding: 5px 0; - - &.active { - background-color: $link_hover_color; - - a { - color: #fff; - font-weight: normal; - text-shadow: none; - border: none; - - &:after { display: none; } - } - } - } - } - } -} diff --git a/app/assets/stylesheets/sections/notes.scss b/app/assets/stylesheets/sections/notes.scss index e1f9c0cb258..74c500f88b3 100644 --- a/app/assets/stylesheets/sections/notes.scss +++ b/app/assets/stylesheets/sections/notes.scss @@ -155,19 +155,26 @@ ul.notes { } .add-diff-note { - background: image-url("diff_note_add.png") no-repeat left 0; - border: none; - height: 22px; - margin-left: -65px; + margin-top: -4px; + @include border-radius(40px); + background: #FFF; + padding: 4px; + font-size: 16px; + color: $link_color; + margin-left: -60px; position: absolute; - width: 22px; z-index: 10; + transition: all 0.2s ease; + // "hide" it by default opacity: 0.0; filter: alpha(opacity=0); &:hover { + font-size: 24px; + background: $bg_primary; + color: #FFF; @include show-add-diff-note; } } diff --git a/app/assets/stylesheets/sections/sidebar.scss b/app/assets/stylesheets/sections/sidebar.scss new file mode 100644 index 00000000000..581a7318ee1 --- /dev/null +++ b/app/assets/stylesheets/sections/sidebar.scss @@ -0,0 +1,169 @@ +.page-with-sidebar { + background: #F5F5F5; +} + +.sidebar-wrapper { + z-index: 99; + overflow-y: auto; + background: #F5F5F5; +} + +.content-wrapper { + width: 100%; + padding: 15px; + background: #FFF; + margin-top: 48px; +} + +.nav-sidebar { + margin: 0; + list-style: none; + + &.navbar-collapse { + padding: 0px !important; + } +} + +.nav-sidebar li a .count { + float: right; + background: #eee; + padding: 0px 8px; + @include border-radius(6px); +} + +.nav-sidebar li { + &.active a { + color: #111; + background: #EEE; + font-weight: bold; + + &.no-highlight { + background: none; + } + + i { + color: #444; + } + } +} + +.nav-sidebar li { + &.separate-item { + border-top: 1px solid #ddd; + padding-top: 10px; + margin-top: 10px; + } + + a { + color: #555; + display: block; + text-decoration: none; + padding: 6px 15px; + font-size: 13px; + line-height: 20px; + text-shadow: 0 1px 2px #FFF; + padding-left: 20px; + + &:hover { + text-decoration: none; + color: #333; + background: #DDD; + } + + &:active, &:focus { + text-decoration: none; + } + + i { + width: 20px; + color: #888; + margin-right: 23px; + } + } +} + +.sidebar-subnav { + margin-left: 0px; + padding-left: 0px; + + li { + line-height: 28px; + font-size: 12px; + list-style: none; + + a { + padding: 5px 15px; + font-size: 12px; + padding-left: 20px; + } + } +} + +@mixin expanded-sidebar { + .page-with-sidebar { + padding-left: 250px; + } + + .sidebar-wrapper { + width: 250px; + position: fixed; + left: 250px; + height: 100%; + margin-left: -250px; + border-right: 1px solid #EAEAEA; + + .nav-sidebar { + margin-top: 20px; + position: fixed; + top: 45px; + width: 250px; + } + } + + .content-wrapper { + padding: 20px; + } +} + +@mixin folded-sidebar { + .page-with-sidebar { + padding-left: 50px; + } + + .sidebar-wrapper { + width: 52px; + position: fixed; + top: 0; + left: 0; + height: 100%; + border-right: 1px solid #EAEAEA; + overflow-x: hidden; + + .nav-sidebar { + margin-top: 20px; + position: absolute; + top: 45px; + width: 52px; + + li a { + padding-left: 18px; + font-size: 14px; + padding: 10px 15px; + text-align: center; + + & > span { + display: none; + } + } + } + } +} + +@media (max-width: $screen-sm-max) { + @include folded-sidebar; +} + +@media(min-width: $screen-sm-max) { + @include expanded-sidebar; +} + diff --git a/app/assets/stylesheets/sections/tree.scss b/app/assets/stylesheets/sections/tree.scss index 678a6cd716d..bc7451e2d53 100644 --- a/app/assets/stylesheets/sections/tree.scss +++ b/app/assets/stylesheets/sections/tree.scss @@ -17,19 +17,6 @@ @include border-radius(0); tr { - td, th { - padding: 8px 10px; - line-height: 20px; - } - th { - font-weight: normal; - font-size: 15px; - border-bottom: 1px solid #CCC !important; - } - td { - border-color: #F1F1F1 !important; - border-bottom: 1px solid; - } &:hover { td { background: $hover; diff --git a/app/assets/stylesheets/sections/votes.scss b/app/assets/stylesheets/sections/votes.scss index d683e33e1f0..ba0a519dca6 100644 --- a/app/assets/stylesheets/sections/votes.scss +++ b/app/assets/stylesheets/sections/votes.scss @@ -37,13 +37,3 @@ margin: 0 8px; } -.votes-holder { - float: right; - width: 250px; - - @media (max-width: $screen-xs-max) { - width: 100%; - margin-top: 5px; - margin-bottom: 10px; - } -} diff --git a/app/assets/stylesheets/themes/ui_basic.scss b/app/assets/stylesheets/themes/ui_basic.scss index 3e3744fdc33..0dad9917b55 100644 --- a/app/assets/stylesheets/themes/ui_basic.scss +++ b/app/assets/stylesheets/themes/ui_basic.scss @@ -9,17 +9,15 @@ .navbar-inner { background: #F1F1F1; border-bottom: 1px solid #DDD; + + .app_logo { + background-color: #DDD; + } + .nav > li > a { color: $style_color; } - .separator { - background: #F9F9F9; - border-left: 1px solid #DDD; - } } } } - .main-nav { - background: #FFF; - } } diff --git a/app/assets/stylesheets/themes/ui_color.scss b/app/assets/stylesheets/themes/ui_color.scss index a08f3ff3d48..3c441a8e098 100644 --- a/app/assets/stylesheets/themes/ui_color.scss +++ b/app/assets/stylesheets/themes/ui_color.scss @@ -23,9 +23,8 @@ background-color: #436; } } - .separator { - background: #436; - border-left: 1px solid #659; + .app_logo { + background-color: #325; } .nav > li > a { color: #98C; diff --git a/app/assets/stylesheets/themes/ui_gray.scss b/app/assets/stylesheets/themes/ui_gray.scss index 959febad6fe..8df08ccaeec 100644 --- a/app/assets/stylesheets/themes/ui_gray.scss +++ b/app/assets/stylesheets/themes/ui_gray.scss @@ -23,9 +23,8 @@ background-color: #272727; } } - .separator { - background: #272727; - border-left: 1px solid #474747; + .app_logo { + background-color: #222; } } } diff --git a/app/assets/stylesheets/themes/ui_mars.scss b/app/assets/stylesheets/themes/ui_mars.scss index 9af5adbf10a..b08cbda6c4f 100644 --- a/app/assets/stylesheets/themes/ui_mars.scss +++ b/app/assets/stylesheets/themes/ui_mars.scss @@ -23,9 +23,8 @@ background-color: #373D47; } } - .separator { - background: #373D47; - border-left: 1px solid #575D67; + .app_logo { + background-color: #24272D; } .nav > li > a { color: #979DA7; diff --git a/app/assets/stylesheets/themes/ui_modern.scss b/app/assets/stylesheets/themes/ui_modern.scss index 308a03477db..34f39614ca4 100644 --- a/app/assets/stylesheets/themes/ui_modern.scss +++ b/app/assets/stylesheets/themes/ui_modern.scss @@ -23,9 +23,8 @@ background-color: #018865; } } - .separator { - background: #018865; - border-left: 1px solid #11A885; + .app_logo { + background-color: #017855; } .nav > li > a { color: #ADC; diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index e6d0c9323c1..8c7d90a5d9f 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -21,7 +21,7 @@ class Admin::GroupsController < Admin::ApplicationController def create @group = Group.new(group_params) - @group.path = @group.name.dup.parameterize if @group.name + @group.name = @group.path.dup unless @group.name if @group.save @group.add_owner(current_user) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index f1e1bebe5ce..4b8cae469e3 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -239,4 +239,63 @@ class ApplicationController < ActionController::Base redirect_to profile_path, notice: 'Please complete your profile with email address' and return end end + + def set_filters_params + params[:sort] ||= 'newest' + params[:scope] = 'all' if params[:scope].blank? + params[:state] = 'opened' if params[:state].blank? + + @filter_params = params.dup + + if @project + @filter_params[:project_id] = @project.id + elsif @group + @filter_params[:group_id] = @group.id + else + # TODO: this filter ignore issues/mr created in public or + # internal repos where you are not a member. Enable this filter + # or improve current implementation to filter only issues you + # created or assigned or mentioned + #@filter_params[:authorized_only] = true + end + + @filter_params + end + + def set_filter_values(collection) + assignee_id = @filter_params[:assignee_id] + author_id = @filter_params[:author_id] + milestone_id = @filter_params[:milestone_id] + + @sort = @filter_params[:sort].try(:humanize) + @assignees = User.where(id: collection.pluck(:assignee_id)) + @authors = User.where(id: collection.pluck(:author_id)) + @milestones = Milestone.where(id: collection.pluck(:milestone_id)) + + if assignee_id.present? && !assignee_id.to_i.zero? + @assignee = @assignees.find_by(id: assignee_id) + end + + if author_id.present? && !author_id.to_i.zero? + @author = @authors.find_by(id: author_id) + end + + if milestone_id.present? && !milestone_id.to_i.zero? + @milestone = @milestones.find_by(id: milestone_id) + end + end + + def get_issues_collection + set_filters_params + issues = IssuesFinder.new.execute(current_user, @filter_params) + set_filter_values(issues) + issues + end + + def get_merge_requests_collection + set_filters_params + merge_requests = MergeRequestsFinder.new.execute(current_user, @filter_params) + set_filter_values(merge_requests) + merge_requests + end end diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index 5aff526d1b5..cd876024ba3 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -3,8 +3,6 @@ class DashboardController < ApplicationController before_filter :load_projects, except: [:projects] before_filter :event_filter, only: :show - before_filter :default_filter, only: [:issues, :merge_requests] - def show # Fetch only 30 projects. @@ -55,13 +53,13 @@ class DashboardController < ApplicationController end def merge_requests - @merge_requests = MergeRequestsFinder.new.execute(current_user, params) + @merge_requests = get_merge_requests_collection @merge_requests = @merge_requests.page(params[:page]).per(20) @merge_requests = @merge_requests.preload(:author, :target_project) end def issues - @issues = IssuesFinder.new.execute(current_user, params) + @issues = get_issues_collection @issues = @issues.page(params[:page]).per(20) @issues = @issues.preload(:author, :project) @@ -76,10 +74,4 @@ class DashboardController < ApplicationController def load_projects @projects = current_user.authorized_projects.sorted_by_activity.non_archived end - - def default_filter - params[:scope] = 'assigned-to-me' if params[:scope].blank? - params[:state] = 'opened' if params[:state].blank? - params[:authorized_only] = true - end end diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 36222758eb2..aad3709090e 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -11,8 +11,6 @@ class GroupsController < ApplicationController # Load group projects before_filter :load_projects, except: [:new, :create, :projects, :edit, :update] - before_filter :default_filter, only: [:issues, :merge_requests] - layout :determine_layout before_filter :set_title, only: [:new, :create] @@ -23,7 +21,7 @@ class GroupsController < ApplicationController def create @group = Group.new(group_params) - @group.path = @group.name.dup.parameterize if @group.name + @group.name = @group.path.dup unless @group.name if @group.save @group.add_owner(current_user) @@ -47,13 +45,13 @@ class GroupsController < ApplicationController end def merge_requests - @merge_requests = MergeRequestsFinder.new.execute(current_user, params) + @merge_requests = get_merge_requests_collection @merge_requests = @merge_requests.page(params[:page]).per(20) @merge_requests = @merge_requests.preload(:author, :target_project) end def issues - @issues = IssuesFinder.new.execute(current_user, params) + @issues = get_issues_collection @issues = @issues.page(params[:page]).per(20) @issues = @issues.preload(:author, :project) @@ -148,18 +146,6 @@ class GroupsController < ApplicationController end end - def default_filter - if params[:scope].blank? - if current_user - params[:scope] = 'assigned-to-me' - else - params[:scope] = 'all' - end - end - params[:state] = 'opened' if params[:state].blank? - params[:group_id] = @group.id - end - def group_params params.require(:group).permit(:name, :description, :path, :avatar) end diff --git a/app/controllers/oauth/applications_controller.rb b/app/controllers/oauth/applications_controller.rb new file mode 100644 index 00000000000..3407490e498 --- /dev/null +++ b/app/controllers/oauth/applications_controller.rb @@ -0,0 +1,41 @@ +class Oauth::ApplicationsController < Doorkeeper::ApplicationsController + before_filter :authenticate_user! + layout "profile" + + def index + head :forbidden and return + end + + def create + @application = Doorkeeper::Application.new(application_params) + + if Doorkeeper.configuration.confirm_application_owner? + @application.owner = current_user + end + + if @application.save + flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create]) + redirect_to oauth_application_url(@application) + else + render :new + end + end + + def destroy + if @application.destroy + flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :destroy]) + end + + redirect_to applications_profile_url + end + + private + + def set_application + @application = current_user.oauth_applications.find(params[:id]) + end + + rescue_from ActiveRecord::RecordNotFound do |exception| + render "errors/not_found", layout: "errors", status: 404 + end +end diff --git a/app/controllers/oauth/authorizations_controller.rb b/app/controllers/oauth/authorizations_controller.rb new file mode 100644 index 00000000000..a57b4a60c24 --- /dev/null +++ b/app/controllers/oauth/authorizations_controller.rb @@ -0,0 +1,57 @@ +class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController + before_filter :authenticate_resource_owner! + layout "profile" + + def new + if pre_auth.authorizable? + if skip_authorization? || matching_token? + auth = authorization.authorize + redirect_to auth.redirect_uri + else + render "doorkeeper/authorizations/new" + end + else + render "doorkeeper/authorizations/error" + end + end + + # TODO: Handle raise invalid authorization + def create + redirect_or_render authorization.authorize + end + + def destroy + redirect_or_render authorization.deny + end + + private + + def matching_token? + Doorkeeper::AccessToken.matching_token_for(pre_auth.client, + current_resource_owner.id, + pre_auth.scopes) + end + + def redirect_or_render(auth) + if auth.redirectable? + redirect_to auth.redirect_uri + else + render json: auth.body, status: auth.status + end + end + + def pre_auth + @pre_auth ||= + Doorkeeper::OAuth::PreAuthorization.new(Doorkeeper.configuration, + server.client_via_uid, + params) + end + + def authorization + @authorization ||= strategy.request + end + + def strategy + @strategy ||= server.authorization_request(pre_auth.response_type) + end +end diff --git a/app/controllers/oauth/authorized_applications_controller.rb b/app/controllers/oauth/authorized_applications_controller.rb new file mode 100644 index 00000000000..0b27ce7da72 --- /dev/null +++ b/app/controllers/oauth/authorized_applications_controller.rb @@ -0,0 +1,8 @@ +class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicationsController + layout "profile" + + def destroy + Doorkeeper::AccessToken.revoke_all_for(params[:id], current_resource_owner) + redirect_to applications_profile_url, notice: I18n.t(:notice, scope: [:doorkeeper, :flash, :authorized_applications, :destroy]) + end +end diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index e877f9b9049..c0b7e2223a2 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -13,6 +13,11 @@ class ProfilesController < ApplicationController def design end + def applications + @applications = current_user.oauth_applications + @authorized_tokens = current_user.oauth_authorized_tokens + end + def update user_params.except!(:email) if @user.ldap_user? diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb index 6b7fe06d59f..7e4580017dd 100644 --- a/app/controllers/projects/application_controller.rb +++ b/app/controllers/projects/application_controller.rb @@ -29,31 +29,4 @@ class Projects::ApplicationController < ApplicationController redirect_to project_tree_path(@project, @ref), notice: "This action is not allowed unless you are on top of a branch" end end - - def set_filter_variables(collection) - params[:sort] ||= 'newest' - params[:scope] = 'all' if params[:scope].blank? - params[:state] = 'opened' if params[:state].blank? - - @sort = params[:sort].humanize - - assignee_id = params[:assignee_id] - author_id = params[:author_id] - milestone_id = params[:milestone_id] - - if assignee_id.present? && !assignee_id.to_i.zero? - @assignee = @project.team.find(assignee_id) - end - - if author_id.present? && !author_id.to_i.zero? - @author = @project.team.find(assignee_id) - end - - if milestone_id.present? && !milestone_id.to_i.zero? - @milestone = @project.milestones.find(milestone_id) - end - - @assignees = User.where(id: collection.pluck(:assignee_id)) - @authors = User.where(id: collection.pluck(:author_id)) - end end diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 22235123826..42e207cf376 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -18,9 +18,7 @@ class Projects::IssuesController < Projects::ApplicationController def index terms = params['issue_search'] - set_filter_variables(@project.issues) - - @issues = IssuesFinder.new.execute(current_user, params.merge(project_id: @project.id)) + @issues = get_issues_collection @issues = @issues.full_search(terms) if terms.present? @issues = @issues.page(params[:page]).per(20) diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 4d6f41e9de5..d23461821d7 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -17,9 +17,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController before_filter :authorize_modify_merge_request!, only: [:close, :edit, :update, :sort] def index - set_filter_variables(@project.merge_requests) - - @merge_requests = MergeRequestsFinder.new.execute(current_user, params.merge(project_id: @project.id)) + @merge_requests = get_merge_requests_collection @merge_requests = @merge_requests.page(params[:page]).per(20) end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index fcff6952d38..e541b6fd872 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -44,6 +44,9 @@ class ProjectsController < ApplicationController def transfer ::Projects::TransferService.new(project, current_user, project_params).execute + if @project.errors[:namespace_id].present? + flash[:alert] = @project.errors[:namespace_id].first + end end def show diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 021bd0a494c..90cc58f44b7 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -114,6 +114,10 @@ module ApplicationHelper Gitlab::Theme.css_class_by_id(current_user.try(:theme_id)) end + def theme_type + Gitlab::Theme.type_css_class_by_id(current_user.try(:theme_id)) + end + def user_color_scheme_class COLOR_SCHEMES[current_user.try(:color_scheme_id)] if defined?(current_user) end @@ -271,4 +275,22 @@ module ApplicationHelper def promo_url 'https://' + promo_host end + + def page_filter_path(options={}) + exist_opts = { + state: params[:state], + scope: params[:scope], + label_name: params[:label_name], + milestone_id: params[:milestone_id], + assignee_id: params[:assignee_id], + author_id: params[:author_id], + sort: params[:sort], + } + + options = exist_opts.merge(options) + + path = request.path + path << "?#{options.to_param}" + path + end end diff --git a/app/helpers/dashboard_helper.rb b/app/helpers/dashboard_helper.rb index acc0eeb76b3..3e6f3b41ff5 100644 --- a/app/helpers/dashboard_helper.rb +++ b/app/helpers/dashboard_helper.rb @@ -1,27 +1,4 @@ module DashboardHelper - def filter_path(entity, options={}) - exist_opts = { - state: params[:state], - scope: params[:scope], - project_id: params[:project_id], - } - - options = exist_opts.merge(options) - - path = request.path - path << "?#{options.to_param}" - path - end - - def entities_per_project(project, entity) - case entity.to_sym - when :issue then @issues.where(project_id: project.id) - when :merge_request then @merge_requests.where(target_project_id: project.id) - else - [] - end.count - end - def projects_dashboard_filter_path(options={}) exist_opts = { sort: params[:sort], @@ -36,32 +13,11 @@ module DashboardHelper path end - def assigned_entities_count(current_user, entity, scope = nil) - items = current_user.send('assigned_' + entity.pluralize) - get_count(items, scope) + def assigned_issues_dashboard_path + issues_dashboard_path(assignee_id: current_user.id) end - def authored_entities_count(current_user, entity, scope = nil) - items = current_user.send(entity.pluralize) - get_count(items, scope) - end - - def authorized_entities_count(current_user, entity, scope = nil) - items = entity.classify.constantize - get_count(items, scope, true, current_user) - end - - protected - - def get_count(items, scope, get_authorized = false, current_user = nil) - items = items.opened - if scope.kind_of?(Group) - items = items.of_group(scope) - elsif scope.kind_of?(Project) - items = items.of_projects(scope) - elsif get_authorized - items = items.of_projects(current_user.authorized_projects) - end - items.count + def assigned_mrs_dashboard_path + merge_requests_dashboard_path(assignee_id: current_user.id) end end diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index cb50d89cba8..a15af0be01a 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -117,4 +117,22 @@ module DiffHelper [comments_left, comments_right] end + + def inline_diff_btn + params_copy = params.dup + params_copy[:view] = 'inline' + + link_to url_for(params_copy), id: "commit-diff-viewtype", class: (params[:view] != 'parallel' ? 'btn active' : 'btn') do + 'Inline' + end + end + + def parallel_diff_btn + params_copy = params.dup + params_copy[:view] = 'parallel' + + link_to url_for(params_copy), id: "commit-diff-viewtype", class: (params[:view] == 'parallel' ? 'btn active' : 'btn') do + 'Side-by-side' + end + end end diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index a3136926b38..903a5009616 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -21,15 +21,14 @@ module EventsHelper def event_filter_link(key, tooltip) key = key.to_s - inactive = if @event_filter.active? key - nil - else - 'inactive' - end + active = if @event_filter.active? key + 'active' + end - content_tag :div, class: "filter_icon #{inactive}" do + content_tag :li, class: "filter_icon #{active}" 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] + content_tag(:i, nil, class: icon_for_event[key]) + + content_tag(:span, ' ' + tooltip) end end end diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb index 0dc53dedeb7..975cdeda1bc 100644 --- a/app/helpers/groups_helper.rb +++ b/app/helpers/groups_helper.rb @@ -6,7 +6,7 @@ module GroupsHelper def leave_group_message(group) "Are you sure you want to leave \"#{group}\" group?" end - + def should_user_see_group_roles?(user, group) if user user.is_admin? || group.members.exists?(user_id: user.id) @@ -44,4 +44,12 @@ module GroupsHelper path << "?#{options.to_param}" path end + + def group_settings_page? + if current_controller?('groups') + current_action?('edit') || current_action?('projects') + else + false + end + end end diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb index 901052edec6..6d2244b8714 100644 --- a/app/helpers/notes_helper.rb +++ b/app/helpers/notes_helper.rb @@ -52,8 +52,11 @@ module NotesHelper discussion_id: discussion_id } - button_tag '', class: 'btn add-diff-note js-add-diff-note-button', - data: data, title: 'Add a comment to this line' + button_tag(class: 'btn add-diff-note js-add-diff-note-button', + data: data, + title: 'Add a comment to this line') do + content_tag :i, nil, class: 'fa fa-comment-o' + end end def link_to_reply_diff(note) diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index fb5470d98e5..e489d431e84 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -68,48 +68,6 @@ module ProjectsHelper project_nav_tabs.include? name end - def selected_label?(label_name) - params[:label_name].to_s.split(',').include?(label_name) - end - - def labels_filter_path(label_name) - label_name = - if selected_label?(label_name) - params[:label_name].split(',').reject { |l| l == label_name }.join(',') - elsif params[:label_name].present? - "#{params[:label_name]},#{label_name}" - else - label_name - end - - project_filter_path(label_name: label_name) - end - - def label_filter_class(label_name) - if selected_label?(label_name) - 'label-filter-item active' - else - 'label-filter-item light' - end - end - - def project_filter_path(options={}) - exist_opts = { - state: params[:state], - scope: params[:scope], - label_name: params[:label_name], - milestone_id: params[:milestone_id], - assignee_id: params[:assignee_id], - sort: params[:sort], - } - - options = exist_opts.merge(options) - - path = request.path - path << "?#{options.to_param}" - path - end - def project_active_milestones @project.milestones.active.order("due_date, title ASC") end diff --git a/app/helpers/tab_helper.rb b/app/helpers/tab_helper.rb index bc43e078568..639fc98c222 100644 --- a/app/helpers/tab_helper.rb +++ b/app/helpers/tab_helper.rb @@ -28,6 +28,10 @@ module TabHelper # nav_link(controller: [:tree, :refs]) { "Hello" } # # => '<li class="active">Hello</li>' # + # # Several paths + # nav_link(path: ['tree#show', 'profile#show']) { "Hello" } + # # => '<li class="active">Hello</li>' + # # # Shorthand path # nav_link(path: 'tree#show') { "Hello" } # # => '<li class="active">Hello</li>' @@ -38,25 +42,7 @@ module TabHelper # # Returns a list item element String def nav_link(options = {}, &block) - if path = options.delete(:path) - if path.respond_to?(:each) - c = path.map { |p| p.split('#').first } - a = path.map { |p| p.split('#').last } - else - c, a, _ = path.split('#') - end - else - c = options.delete(:controller) - a = options.delete(:action) - end - - if c && a - # When given both options, make sure BOTH are active - klass = current_controller?(*c) && current_action?(*a) ? 'active' : '' - else - # Otherwise check EITHER option - klass = current_controller?(*c) || current_action?(*a) ? 'active' : '' - end + klass = active_nav_link?(options) ? 'active' : '' # Add our custom class into the html_options, which may or may not exist # and which may or may not already have a :class key @@ -72,6 +58,34 @@ module TabHelper end end + def active_nav_link?(options) + if path = options.delete(:path) + unless path.respond_to?(:each) + path = [path] + end + + path.any? do |single_path| + current_path?(single_path) + end + else + c = options.delete(:controller) + a = options.delete(:action) + + if c && a + # When given both options, make sure BOTH are true + current_controller?(*c) && current_action?(*a) + else + # Otherwise check EITHER option + current_controller?(*c) || current_action?(*a) + end + end + end + + def current_path?(path) + c, a, _ = path.split('#') + current_controller?(c) && current_action?(a) + end + def project_tab_class return "active" if current_page?(controller: "/projects", action: :edit, id: @project) diff --git a/app/helpers/tree_helper.rb b/app/helpers/tree_helper.rb index 8e209498323..e32aeba5f8f 100644 --- a/app/helpers/tree_helper.rb +++ b/app/helpers/tree_helper.rb @@ -53,13 +53,41 @@ module TreeHelper File.join(*args) end - def allowed_tree_edit? - return false unless @repository.branch_names.include?(@ref) + def allowed_tree_edit?(project = nil, ref = nil) + project ||= @project + ref ||= @ref + return false unless project.repository.branch_names.include?(ref) - if @project.protected_branch? @ref - can?(current_user, :push_code_to_protected_branches, @project) + if project.protected_branch? ref + can?(current_user, :push_code_to_protected_branches, project) else - can?(current_user, :push_code, @project) + can?(current_user, :push_code, project) + end + end + + def edit_blob_link(project, ref, path, options = {}) + blob = + begin + project.repository.blob_at(ref, path) + rescue + nil + end + + if blob && blob.text? + text = 'Edit' + after = options[:after] || '' + from_mr = options[:from_merge_request_id] + link_opts = {} + link_opts[:from_merge_request_id] = from_mr if from_mr + cls = 'btn btn-small' + if allowed_tree_edit?(project, ref) + link_to text, project_edit_tree_path(project, tree_join(ref, path), + link_opts), class: cls + else + content_tag :span, text, class: cls + ' disabled' + end + after.html_safe + else + '' end end diff --git a/app/models/commit.rb b/app/models/commit.rb index 212229649fc..37dd371ec00 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -10,12 +10,12 @@ class Commit # Used to prevent 500 error on huge commits by suppressing diff # # User can force display of diff above this size - DIFF_SAFE_FILES = 100 - DIFF_SAFE_LINES = 5000 + DIFF_SAFE_FILES = 100 unless defined?(DIFF_SAFE_FILES) + DIFF_SAFE_LINES = 5000 unless defined?(DIFF_SAFE_LINES) # Commits above this size will not be rendered in HTML - DIFF_HARD_LIMIT_FILES = 1000 - DIFF_HARD_LIMIT_LINES = 50000 + DIFF_HARD_LIMIT_FILES = 1000 unless defined?(DIFF_HARD_LIMIT_FILES) + DIFF_HARD_LIMIT_LINES = 50000 unless defined?(DIFF_HARD_LIMIT_LINES) class << self def decorate(commits) diff --git a/app/models/note.rb b/app/models/note.rb index 5bf645bbd1d..5996298be22 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -502,6 +502,6 @@ class Note < ActiveRecord::Base end def editable? - !system + !read_attribute(:system) end end diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb index 770a26ed894..f8a28ca9866 100644 --- a/app/models/project_wiki.rb +++ b/app/models/project_wiki.rb @@ -5,7 +5,7 @@ class ProjectWiki 'Markdown' => :markdown, 'RDoc' => :rdoc, 'AsciiDoc' => :asciidoc - } + } unless defined?(MARKUPS) class CouldNotCreateWikiError < StandardError; end diff --git a/app/models/user.rb b/app/models/user.rb index 7faeef1b5b0..7dae318e780 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -106,6 +106,7 @@ class User < ActiveRecord::Base has_many :recent_events, -> { order "id DESC" }, foreign_key: :author_id, class_name: "Event" has_many :assigned_issues, dependent: :destroy, foreign_key: :assignee_id, class_name: "Issue" has_many :assigned_merge_requests, dependent: :destroy, foreign_key: :assignee_id, class_name: "MergeRequest" + has_many :oauth_applications, class_name: 'Doorkeeper::Application', as: :owner, dependent: :destroy # @@ -564,4 +565,8 @@ class User < ActiveRecord::Base namespaces += masters_groups end end + + def oauth_authorized_tokens + Doorkeeper::AccessToken.where(resource_owner_id: self.id, revoked_at: nil) + end end diff --git a/app/services/oauth2/access_token_validation_service.rb b/app/services/oauth2/access_token_validation_service.rb new file mode 100644 index 00000000000..95283489753 --- /dev/null +++ b/app/services/oauth2/access_token_validation_service.rb @@ -0,0 +1,41 @@ +module Oauth2::AccessTokenValidationService + # Results: + VALID = :valid + EXPIRED = :expired + REVOKED = :revoked + INSUFFICIENT_SCOPE = :insufficient_scope + + class << self + def validate(token, scopes: []) + if token.expired? + return EXPIRED + + elsif token.revoked? + return REVOKED + + elsif !self.sufficent_scope?(token, scopes) + return INSUFFICIENT_SCOPE + + else + return VALID + end + end + + protected + # True if the token's scope is a superset of required scopes, + # or the required scopes is empty. + def sufficent_scope?(token, scopes) + if scopes.blank? + # if no any scopes required, the scopes of token is sufficient. + return true + else + # If there are scopes required, then check whether + # the set of authorized scopes is a superset of the set of required scopes + required_scopes = Set.new(scopes) + authorized_scopes = Set.new(token.scopes) + + return authorized_scopes >= required_scopes + end + end + end +end
\ No newline at end of file diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb index 3672b623806..31226b7504b 100644 --- a/app/services/projects/create_service.rb +++ b/app/services/projects/create_service.rb @@ -12,12 +12,17 @@ module Projects @project.visibility_level = default_features.visibility_level end - # Parametrize path for project - # - # Ex. - # 'GitLab HQ'.parameterize => "gitlab-hq" - # - @project.path = @project.name.dup.parameterize unless @project.path.present? + # Set project name from path + if @project.name.present? && @project.path.present? + # if both name and path set - everything is ok + elsif @project.path.present? + # Set project name from path + @project.name = @project.path.dup + elsif @project.name.present? + # For compatibility - set path from name + # TODO: remove this in 8.0 + @project.path = @project.name.dup.parameterize + end # get namespace id namespace_id = params[:namespace_id] diff --git a/app/views/admin/groups/_form.html.haml b/app/views/admin/groups/_form.html.haml index f4d7e25fd74..86a73200609 100644 --- a/app/views/admin/groups/_form.html.haml +++ b/app/views/admin/groups/_form.html.haml @@ -21,17 +21,6 @@ = link_to 'Cancel', admin_groups_path, class: "btn btn-cancel" - else - .form-group.group_name_holder - = f.label :path, class: 'control-label' do - %span Group path - .col-sm-10 - = f.text_field :path, placeholder: "example-group", class: "form-control danger" - .bs-callout.bs-callout-danger - %ul - %li Changing group path can have unintended side effects. - %li Renaming group path will rename directory for all related projects - %li It will change web url for access group and group projects. - %li It will change the git path to repositories under this group. .form-actions = f.submit 'Save changes', class: "btn btn-primary" = link_to 'Cancel', admin_group_path(@group), class: "btn btn-cancel" diff --git a/app/views/dashboard/_groups.html.haml b/app/views/dashboard/_groups.html.haml index 5460cf56f22..ddabd6e0d52 100644 --- a/app/views/dashboard/_groups.html.haml +++ b/app/views/dashboard/_groups.html.haml @@ -1,10 +1,11 @@ .panel.panel-default .panel-heading.clearfix - = search_field_tag :filter_group, nil, placeholder: 'Filter by name', class: 'dash-filter form-control' - - if current_user.can_create_group? - = link_to new_group_path, class: "btn btn-new pull-right" do - %i.fa.fa-plus - New group + .input-group + = search_field_tag :filter_group, nil, placeholder: 'Filter by name', class: 'dash-filter form-control' + - if current_user.can_create_group? + .input-group-addon + = link_to new_group_path, class: "" do + %strong 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 3598425777f..304aa17eba8 100644 --- a/app/views/dashboard/_projects.html.haml +++ b/app/views/dashboard/_projects.html.haml @@ -1,10 +1,11 @@ .panel.panel-default .panel-heading.clearfix - = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'dash-filter form-control' - - if current_user.can_create_project? - = link_to new_project_path, class: "btn btn-new pull-right" do - %i.fa.fa-plus - New project + .input-group + = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'dash-filter form-control' + - if current_user.can_create_project? + .input-group-addon + = link_to new_project_path, class: "" do + %strong New project %ul.well-list.dash-list - projects.each do |project| diff --git a/app/views/dashboard/_sidebar.html.haml b/app/views/dashboard/_sidebar.html.haml index add9eb7fa29..a980f495427 100644 --- a/app/views/dashboard/_sidebar.html.haml +++ b/app/views/dashboard/_sidebar.html.haml @@ -15,11 +15,4 @@ = render "groups", groups: @groups .prepend-top-20 - %span.rss-icon - = link_to dashboard_path(:atom, { private_token: current_user.private_token }) do - %strong - %i.fa.fa-rss - News Feed - -%hr -= render 'shared/promo' + = render 'shared/promo' diff --git a/app/views/dashboard/issues.html.haml b/app/views/dashboard/issues.html.haml index 7c1f1ddbb80..db19a46cb26 100644 --- a/app/views/dashboard/issues.html.haml +++ b/app/views/dashboard/issues.html.haml @@ -5,10 +5,6 @@ List all issues from all projects you have access to. %hr -.row - .fixed.sidebar-expand-button.hidden-lg.hidden-md - %i.fa.fa-list.fa-2x - .col-md-3.responsive-side - = render 'shared/filter', entity: 'issue' - .col-md-9 - = render 'shared/issues' +.append-bottom-20 + = render 'shared/issuable_filter' += render 'shared/issues' diff --git a/app/views/dashboard/merge_requests.html.haml b/app/views/dashboard/merge_requests.html.haml index c96584c7b6b..97a42461b4e 100644 --- a/app/views/dashboard/merge_requests.html.haml +++ b/app/views/dashboard/merge_requests.html.haml @@ -5,10 +5,6 @@ %p.light List all merge requests from all projects you have access to. %hr -.row - .fixed.sidebar-expand-button.hidden-lg.hidden-md - %i.fa.fa-list.fa-2x - .col-md-3.responsive-side - = render 'shared/filter', entity: 'merge_request' - .col-md-9 - = render 'shared/merge_requests' +.append-bottom-20 + = render 'shared/issuable_filter' += render 'shared/merge_requests' diff --git a/app/views/dashboard/projects.html.haml b/app/views/dashboard/projects.html.haml index 5b7835b097b..b880acf1245 100644 --- a/app/views/dashboard/projects.html.haml +++ b/app/views/dashboard/projects.html.haml @@ -38,17 +38,19 @@ = link_to project_path(project), class: dom_class(project) do = project.name_with_namespace + - if project.forked_from_project + + %small + %i.fa.fa-code-fork + Forked from: + = link_to project.forked_from_project.name_with_namespace, project_path(project.forked_from_project) + - if current_user.can_leave_project?(project) .pull-right = link_to leave_project_team_members_path(project), data: { confirm: "Leave project?"}, method: :delete, remote: true, class: "btn-tiny btn remove-row", title: 'Leave project' do %i.fa.fa-sign-out Leave - - if project.forked_from_project - %small.pull-right - %i.fa.fa-code-fork - Forked from: - = link_to project.forked_from_project.name_with_namespace, project_path(project.forked_from_project) .project-info .pull-right - if project.archived? diff --git a/app/views/doorkeeper/applications/_delete_form.html.haml b/app/views/doorkeeper/applications/_delete_form.html.haml new file mode 100644 index 00000000000..bf8098f38d0 --- /dev/null +++ b/app/views/doorkeeper/applications/_delete_form.html.haml @@ -0,0 +1,4 @@ +- submit_btn_css ||= 'btn btn-link btn-remove btn-small' += form_tag oauth_application_path(application) do + %input{:name => "_method", :type => "hidden", :value => "delete"}/ + = submit_tag 'Destroy', onclick: "return confirm('Are you sure?')", class: submit_btn_css
\ No newline at end of file diff --git a/app/views/doorkeeper/applications/_form.html.haml b/app/views/doorkeeper/applications/_form.html.haml new file mode 100644 index 00000000000..a5fec2fabdb --- /dev/null +++ b/app/views/doorkeeper/applications/_form.html.haml @@ -0,0 +1,24 @@ += form_for application, url: doorkeeper_submit_path(application), html: {class: 'form-horizontal', role: 'form'} do |f| + - if application.errors.any? + .alert.alert-danger{"data-alert" => ""} + %p Whoops! Check your form for possible errors + = content_tag :div, class: "form-group#{' has-error' if application.errors[:name].present?}" do + = f.label :name, class: 'col-sm-2 control-label' + .col-sm-10 + = f.text_field :name, class: 'form-control' + = doorkeeper_errors_for application, :name + = content_tag :div, class: "form-group#{' has-error' if application.errors[:redirect_uri].present?}" do + = f.label :redirect_uri, class: 'col-sm-2 control-label' + .col-sm-10 + = f.text_area :redirect_uri, class: 'form-control' + = doorkeeper_errors_for application, :redirect_uri + %span.help-block + Use one line per URI + - if Doorkeeper.configuration.native_redirect_uri + %span.help-block + Use + %code= Doorkeeper.configuration.native_redirect_uri + for local tests + .form-actions + = f.submit 'Submit', class: "btn btn-primary wide" + = link_to "Cancel", applications_profile_path, class: "btn btn-default" diff --git a/app/views/doorkeeper/applications/edit.html.haml b/app/views/doorkeeper/applications/edit.html.haml new file mode 100644 index 00000000000..61584eb9c49 --- /dev/null +++ b/app/views/doorkeeper/applications/edit.html.haml @@ -0,0 +1,2 @@ +%h3.page-title Edit application += render 'form', application: @application
\ No newline at end of file diff --git a/app/views/doorkeeper/applications/index.html.haml b/app/views/doorkeeper/applications/index.html.haml new file mode 100644 index 00000000000..e5be4b4bcac --- /dev/null +++ b/app/views/doorkeeper/applications/index.html.haml @@ -0,0 +1,16 @@ +%h3.page-title Your applications +%p= link_to 'New Application', new_oauth_application_path, class: 'btn btn-success' +%table.table.table-striped + %thead + %tr + %th Name + %th Callback URL + %th + %th + %tbody + - @applications.each do |application| + %tr{:id => "application_#{application.id}"} + %td= link_to application.name, oauth_application_path(application) + %td= application.redirect_uri + %td= link_to 'Edit', edit_oauth_application_path(application), class: 'btn btn-link' + %td= render 'delete_form', application: application
\ No newline at end of file diff --git a/app/views/doorkeeper/applications/new.html.haml b/app/views/doorkeeper/applications/new.html.haml new file mode 100644 index 00000000000..655845e4af5 --- /dev/null +++ b/app/views/doorkeeper/applications/new.html.haml @@ -0,0 +1,2 @@ +%h3.page-title New application += render 'form', application: @application
\ No newline at end of file diff --git a/app/views/doorkeeper/applications/show.html.haml b/app/views/doorkeeper/applications/show.html.haml new file mode 100644 index 00000000000..82e78b4af13 --- /dev/null +++ b/app/views/doorkeeper/applications/show.html.haml @@ -0,0 +1,26 @@ +%h3.page-title + Application: #{@application.name} + + +%table.table + %tr + %td + Application Id + %td + %code#application_id= @application.uid + %tr + %td + Secret: + %td + %code#secret= @application.secret + + %tr + %td + Callback url + %td + - @application.redirect_uri.split.each do |uri| + %div + %span.monospace= uri +.form-actions + = link_to 'Edit', edit_oauth_application_path(@application), class: 'btn btn-primary wide pull-left' + = render 'delete_form', application: @application, submit_btn_css: 'btn btn-danger prepend-left-10' diff --git a/app/views/doorkeeper/authorizations/error.html.haml b/app/views/doorkeeper/authorizations/error.html.haml new file mode 100644 index 00000000000..7561ec85ed9 --- /dev/null +++ b/app/views/doorkeeper/authorizations/error.html.haml @@ -0,0 +1,3 @@ +%h3.page-title An error has occurred +%main{:role => "main"} + %pre= @pre_auth.error_response.body[:error_description]
\ No newline at end of file diff --git a/app/views/doorkeeper/authorizations/new.html.haml b/app/views/doorkeeper/authorizations/new.html.haml new file mode 100644 index 00000000000..15f9ee266c1 --- /dev/null +++ b/app/views/doorkeeper/authorizations/new.html.haml @@ -0,0 +1,28 @@ +%h3.page-title Authorize required +%main{:role => "main"} + %p.h4 + Authorize + %strong.text-info= @pre_auth.client.name + to use your account? + - if @pre_auth.scopes + #oauth-permissions + %p This application will be able to: + %ul.text-info + - @pre_auth.scopes.each do |scope| + %li= t scope, scope: [:doorkeeper, :scopes] + %hr/ + .actions + = form_tag oauth_authorization_path, method: :post do + = hidden_field_tag :client_id, @pre_auth.client.uid + = hidden_field_tag :redirect_uri, @pre_auth.redirect_uri + = hidden_field_tag :state, @pre_auth.state + = hidden_field_tag :response_type, @pre_auth.response_type + = hidden_field_tag :scope, @pre_auth.scope + = submit_tag "Authorize", class: "btn btn-success wide pull-left" + = form_tag oauth_authorization_path, method: :delete do + = hidden_field_tag :client_id, @pre_auth.client.uid + = hidden_field_tag :redirect_uri, @pre_auth.redirect_uri + = hidden_field_tag :state, @pre_auth.state + = hidden_field_tag :response_type, @pre_auth.response_type + = hidden_field_tag :scope, @pre_auth.scope + = submit_tag "Deny", class: "btn btn-danger prepend-left-10"
\ No newline at end of file diff --git a/app/views/doorkeeper/authorizations/show.html.haml b/app/views/doorkeeper/authorizations/show.html.haml new file mode 100644 index 00000000000..9a402007194 --- /dev/null +++ b/app/views/doorkeeper/authorizations/show.html.haml @@ -0,0 +1,3 @@ +%h3.page-title Authorization code: +%main{:role => "main"} + %code#authorization_code= params[:code]
\ No newline at end of file diff --git a/app/views/doorkeeper/authorized_applications/_delete_form.html.haml b/app/views/doorkeeper/authorized_applications/_delete_form.html.haml new file mode 100644 index 00000000000..5cbb4a70c19 --- /dev/null +++ b/app/views/doorkeeper/authorized_applications/_delete_form.html.haml @@ -0,0 +1,4 @@ +- submit_btn_css ||= 'btn btn-link btn-remove' += form_tag oauth_authorized_application_path(application) do + %input{:name => "_method", :type => "hidden", :value => "delete"}/ + = submit_tag 'Revoke', onclick: "return confirm('Are you sure?')", class: 'btn btn-link btn-remove btn-small'
\ No newline at end of file diff --git a/app/views/doorkeeper/authorized_applications/index.html.haml b/app/views/doorkeeper/authorized_applications/index.html.haml new file mode 100644 index 00000000000..814cdc987ef --- /dev/null +++ b/app/views/doorkeeper/authorized_applications/index.html.haml @@ -0,0 +1,16 @@ +%header.page-header + %h1 Your authorized applications +%main{:role => "main"} + %table.table.table-striped + %thead + %tr + %th Application + %th Created At + %th + %th + %tbody + - @applications.each do |application| + %tr + %td= application.name + %td= application.created_at.strftime('%Y-%m-%d %H:%M:%S') + %td= render 'delete_form', application: application
\ No newline at end of file diff --git a/app/views/groups/_settings_nav.html.haml b/app/views/groups/_settings_nav.html.haml index ec1fb4a2c00..82d760f7c41 100644 --- a/app/views/groups/_settings_nav.html.haml +++ b/app/views/groups/_settings_nav.html.haml @@ -1,4 +1,4 @@ -%ul.nav.nav-pills.nav-stacked.nav-stacked-menu +%ul.sidebar-subnav = nav_link(path: 'groups#edit') do = link_to edit_group_path(@group) do %i.fa.fa-pencil-square-o diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml index eb24fd65d9e..a963c59586e 100644 --- a/app/views/groups/edit.html.haml +++ b/app/views/groups/edit.html.haml @@ -1,41 +1,37 @@ -.row - .col-md-2 - = render 'settings_nav' - .col-md-10 - .panel.panel-default - .panel-heading - %strong= @group.name - group settings: - .panel-body - = form_for @group, html: { multipart: true, class: "form-horizontal" }, authenticity_token: true do |f| - - if @group.errors.any? - .alert.alert-danger - %span= @group.errors.full_messages.first - = render 'shared/group_form', f: f +.panel.panel-default + .panel-heading + %strong= @group.name + group settings: + .panel-body + = form_for @group, html: { multipart: true, class: "form-horizontal" }, authenticity_token: true do |f| + - if @group.errors.any? + .alert.alert-danger + %span= @group.errors.full_messages.first + = render 'shared/group_form', f: f - .form-group - .col-sm-2 - .col-sm-10 - = image_tag group_icon(@group.to_param), alt: '', class: 'avatar s160' - %p.light - - if @group.avatar? - You can change your group avatar here - - else - You can upload a group avatar here - = render 'shared/choose_group_avatar_button', f: f - - if @group.avatar? - %hr - = link_to 'Remove avatar', group_avatar_path(@group.to_param), data: { confirm: "Group avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-small remove-avatar" + .form-group + .col-sm-2 + .col-sm-10 + = image_tag group_icon(@group.to_param), alt: '', class: 'avatar s160' + %p.light + - if @group.avatar? + You can change your group avatar here + - else + You can upload a group avatar here + = render 'shared/choose_group_avatar_button', f: f + - if @group.avatar? + %hr + = link_to 'Remove avatar', group_avatar_path(@group.to_param), data: { confirm: "Group avatar will be removed. Are you sure?"}, method: :delete, class: "btn btn-remove btn-small remove-avatar" - .form-actions - = f.submit 'Save group', class: "btn btn-save" + .form-actions + = f.submit 'Save group', class: "btn btn-save" - .panel.panel-danger - .panel-heading Remove group - .panel-body - %p - Removing group will cause all child projects and resources to be removed. - %br - %strong Removed group can not be restored! +.panel.panel-danger + .panel-heading Remove group + .panel-body + %p + Removing group will cause all child projects and resources to be removed. + %br + %strong Removed group can not be restored! - = link_to 'Remove Group', @group, data: {confirm: 'Removed group can not be restored! Are you sure?'}, method: :delete, class: "btn btn-remove" + = link_to 'Remove Group', @group, data: {confirm: 'Removed group can not be restored! Are you sure?'}, method: :delete, class: "btn btn-remove" diff --git a/app/views/groups/issues.html.haml b/app/views/groups/issues.html.haml index 1932ba2f644..6c0d89c4e7c 100644 --- a/app/views/groups/issues.html.haml +++ b/app/views/groups/issues.html.haml @@ -9,10 +9,6 @@ To see all issues you should visit #{link_to 'dashboard', issues_dashboard_path} page. %hr -.row - .fixed.sidebar-expand-button.hidden-lg.hidden-md - %i.fa.fa-list.fa-2x - .col-md-3.responsive-side - = render 'shared/filter', entity: 'issue' - .col-md-9 - = render 'shared/issues' +.append-bottom-20 + = render 'shared/issuable_filter' += render 'shared/issues' diff --git a/app/views/groups/merge_requests.html.haml b/app/views/groups/merge_requests.html.haml index 86d5acdaa32..1ad74905636 100644 --- a/app/views/groups/merge_requests.html.haml +++ b/app/views/groups/merge_requests.html.haml @@ -8,10 +8,6 @@ - if current_user To see all merge requests you should visit #{link_to 'dashboard', merge_requests_dashboard_path} page. %hr -.row - .fixed.sidebar-expand-button.hidden-lg.hidden-md - %i.fa.fa-list.fa-2x - .col-md-3.responsive-side - = render 'shared/filter', entity: 'merge_request' - .col-md-9 - = render 'shared/merge_requests' +.append-bottom-20 + = render 'shared/issuable_filter' += render 'shared/merge_requests' diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml index 411d1822be0..7bcac56c37b 100644 --- a/app/views/groups/milestones/show.html.haml +++ b/app/views/groups/milestones/show.html.haml @@ -1,4 +1,9 @@ -%h3.page-title +%h4.page-title + .issue-box{ class: "issue-box-#{@group_milestone.closed? ? 'closed' : 'open'}" } + - if @group_milestone.closed? + Closed + - else + Open Milestone #{@group_milestone.title} .pull-right - if can?(current_user, :manage_group, @group) @@ -7,46 +12,41 @@ - else = link_to 'Reopen Milestone', group_milestone_path(@group, @group_milestone.safe_title, title: @group_milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-small btn-grouped btn-reopen" +%hr - if (@group_milestone.total_items_count == @group_milestone.closed_items_count) && @group_milestone.active? .alert.alert-success %span All issues for this milestone are closed. You may close the milestone now. -.back-link - = link_to group_milestones_path(@group) do - ← To milestones list - -.issue-box{ class: "issue-box-#{@group_milestone.closed? ? 'closed' : 'open'}" } - .state.clearfix - .state-label - - if @group_milestone.closed? - Closed - - else - Open - - %h4.title - = gfm escape_once(@group_milestone.title) - - .description - - @group_milestone.milestones.each do |milestone| - %hr - %h4 - = link_to "#{milestone.project.name} - #{milestone.title}", project_milestone_path(milestone.project, milestone) - %span.pull-right= milestone.expires_at +.description +%table.table + %thead + %tr + %th Project + %th Open issues + %th State + %th Due date + - @group_milestone.milestones.each do |milestone| + %tr + %td + = link_to "#{milestone.project.name}", project_milestone_path(milestone.project, milestone) + %td + = milestone.issues.opened.count + %td - if milestone.closed? - %span.label.label-danger #{milestone.state} - = preserve do - - if milestone.description.present? - = milestone.description - - .context - %p - Progress: - #{@group_milestone.closed_items_count} closed - – - #{@group_milestone.open_items_count} open + Closed + - else + Open + %td + = milestone.expires_at - .progress.progress-info - .progress-bar{style: "width: #{@group_milestone.percent_complete}%;"} +.context + %p.lead + Progress: + #{@group_milestone.closed_items_count} closed + – + #{@group_milestone.open_items_count} open + .progress.progress-info + .progress-bar{style: "width: #{@group_milestone.percent_complete}%;"} %ul.nav.nav-tabs %li.active diff --git a/app/views/groups/projects.html.haml b/app/views/groups/projects.html.haml index 65a66355c56..40c81e8cd5b 100644 --- a/app/views/groups/projects.html.haml +++ b/app/views/groups/projects.html.haml @@ -1,29 +1,25 @@ -.row - .col-md-2 - = render 'settings_nav' - .col-md-10 - .panel.panel-default - .panel-heading - %strong= @group.name - projects: - - if can? current_user, :manage_group, @group - .panel-head-actions - = link_to new_project_path(namespace_id: @group.id), class: "btn btn-new" do - %i.fa.fa-plus - New Project - %ul.well-list - - @projects.each do |project| - %li - .list-item-name - = visibility_level_icon(project.visibility_level) - %strong= link_to project.name_with_namespace, project - %span.label.label-gray - = repository_size(project) - .pull-right - = 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, data: { confirm: remove_project_message(project)}, method: :delete, class: "btn btn-small btn-remove" - - if @projects.blank? - .nothing-here-block This group has no projects yet +.panel.panel-default + .panel-heading + %strong= @group.name + projects: + - if can? current_user, :manage_group, @group + .panel-head-actions + = link_to new_project_path(namespace_id: @group.id), class: "btn btn-new" do + %i.fa.fa-plus + New Project + %ul.well-list + - @projects.each do |project| + %li + .list-item-name + = visibility_level_icon(project.visibility_level) + %strong= link_to project.name_with_namespace, project + %span.label.label-gray + = repository_size(project) + .pull-right + = 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, data: { confirm: remove_project_message(project)}, method: :delete, class: "btn btn-small btn-remove" + - if @projects.blank? + .nothing-here-block This group has no projects yet - = paginate @projects, theme: "gitlab" += paginate @projects, theme: "gitlab" diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index d876e87852c..81f0e1dd2d8 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -1,37 +1,22 @@ .dashboard - %section.activities.col-md-8.hidden-sm.hidden-xs - - if current_user - = render "events/event_last_push", event: @last_push - = link_to dashboard_path, class: 'btn btn-tiny' do - ← To dashboard - - %span.cgray - Currently you are only seeing events from the - = @group.name - group - %hr - = render 'shared/event_filter' - - if @events.any? - .content_list - - else - .nothing-here-block Project activity will be displayed here - = spinner - %aside.side.col-md-4 - .light-well.append-bottom-20 - = image_tag group_icon(@group.path), class: "avatar s90" - .clearfix.light - %h3.page-title - = @group.name - - if @group.description.present? - %p - = escaped_autolink(@group.description) - = render "projects", projects: @projects - - if current_user - .prepend-top-20 - = link_to group_path(@group, { format: :atom, private_token: current_user.private_token }), title: "Feed" do - %strong - %i.fa.fa-rss - News Feed - - %hr - = render 'shared/promo' + %div + = image_tag group_icon(@group.path), class: "avatar s90" + .clearfix + %h2 + = @group.name + - if @group.description.present? + %p + = escaped_autolink(@group.description) + %hr + .row + %section.activities.col-md-8.hidden-sm.hidden-xs + - if current_user + = render "events/event_last_push", event: @last_push + = render 'shared/event_filter' + - if @events.any? + .content_list + - else + .nothing-here-block Project activity will be displayed here + = spinner + %aside.side.col-md-4 + = render "projects", projects: @projects diff --git a/app/views/layouts/_broadcast.html.haml b/app/views/layouts/_broadcast.html.haml index e7d477c225e..e589e34dd23 100644 --- a/app/views/layouts/_broadcast.html.haml +++ b/app/views/layouts/_broadcast.html.haml @@ -2,3 +2,7 @@ .broadcast-message{ style: broadcast_styling(broadcast_message) } %i.fa.fa-bullhorn = broadcast_message.message + :css + .sidebar-wrapper .nav-sidebar { + margin-top: 58px; + } diff --git a/app/views/layouts/_head_panel.html.haml b/app/views/layouts/_head_panel.html.haml index 5dcaee2fa02..eda37f8237a 100644 --- a/app/views/layouts/_head_panel.html.haml +++ b/app/views/layouts/_head_panel.html.haml @@ -2,10 +2,8 @@ .navbar-inner .container %div.app_logo - %span.separator = link_to root_path, class: "home has_bottom_tooltip", title: "Dashboard" do %h1 GITLAB - %span.separator %h1.title= title %button.navbar-toggle{"data-target" => ".navbar-collapse", "data-toggle" => "collapse", type: "button"} diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml new file mode 100644 index 00000000000..621365fa6aa --- /dev/null +++ b/app/views/layouts/_page.html.haml @@ -0,0 +1,16 @@ +- if defined?(sidebar) + .page-with-sidebar + .sidebar-wrapper + = render(sidebar) + .content-wrapper + .container-fluid + .content + = render "layouts/flash" + .clearfix + = yield +- else + .container.navless-container + .content + = yield + += yield :embedded_scripts diff --git a/app/views/layouts/admin.html.haml b/app/views/layouts/admin.html.haml index 207ab22f4c7..fb62d5fea0a 100644 --- a/app/views/layouts/admin.html.haml +++ b/app/views/layouts/admin.html.haml @@ -1,13 +1,7 @@ !!! 5 %html{ lang: "en"} = render "layouts/head", title: "Admin area" - %body{class: "#{app_theme} admin", :'data-page' => body_data_page} + %body{class: "#{app_theme} #{theme_type} admin", :'data-page' => body_data_page} = render "layouts/broadcast" = render "layouts/head_panel", title: "Admin area" - %nav.main-nav.navbar-collapse.collapse - .container= render 'layouts/nav/admin' - .container - .content - = render "layouts/flash" - = yield - = yield :embedded_scripts + = render 'layouts/page', sidebar: 'layouts/nav/admin' diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index 7d0819aa93e..d40c9753b10 100644 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -1,12 +1,7 @@ !!! 5 %html{ lang: "en"} = render "layouts/head", title: "Dashboard" - %body{class: "#{app_theme} application", :'data-page' => body_data_page } + %body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page } = render "layouts/broadcast" = render "layouts/head_panel", title: "Dashboard" - %nav.main-nav.navbar-collapse.collapse - .container= render 'layouts/nav/dashboard' - .container - .content - = render "layouts/flash" - = yield + = render 'layouts/page', sidebar: 'layouts/nav/dashboard' diff --git a/app/views/layouts/doorkeeper/admin.html.haml b/app/views/layouts/doorkeeper/admin.html.haml new file mode 100644 index 00000000000..bd9adfab66d --- /dev/null +++ b/app/views/layouts/doorkeeper/admin.html.haml @@ -0,0 +1,22 @@ +!!! +%html + %head + %meta{:charset => "utf-8"} + %meta{:content => "IE=edge", "http-equiv" => "X-UA-Compatible"} + %meta{:content => "width=device-width, initial-scale=1.0", :name => "viewport"} + %title Doorkeeper + = stylesheet_link_tag "doorkeeper/admin/application" + = csrf_meta_tags + %body + .navbar.navbar-inverse.navbar-fixed-top{:role => "navigation"} + .container + .navbar-header + = link_to 'OAuth2 Provider', oauth_applications_path, class: 'navbar-brand' + %ul.nav.navbar-nav + = content_tag :li, class: "#{'active' if request.path == oauth_applications_path}" do + = link_to 'Applications', oauth_applications_path + .container + - if flash[:notice].present? + .alert.alert-info + = flash[:notice] + = yield
\ No newline at end of file diff --git a/app/views/layouts/doorkeeper/application.html.haml b/app/views/layouts/doorkeeper/application.html.haml new file mode 100644 index 00000000000..e5f37fad1f4 --- /dev/null +++ b/app/views/layouts/doorkeeper/application.html.haml @@ -0,0 +1,15 @@ +!!! +%html + %head + %title OAuth authorize required + %meta{:charset => "utf-8"} + %meta{:content => "IE=edge", "http-equiv" => "X-UA-Compatible"} + %meta{:content => "width=device-width, initial-scale=1.0", :name => "viewport"} + = stylesheet_link_tag "doorkeeper/application" + = csrf_meta_tags + %body + #container + - if flash[:notice].present? + .alert.alert-info + = flash[:notice] + = yield
\ No newline at end of file diff --git a/app/views/layouts/errors.html.haml b/app/views/layouts/errors.html.haml index 16df9c10fbb..e7d875173e6 100644 --- a/app/views/layouts/errors.html.haml +++ b/app/views/layouts/errors.html.haml @@ -1,7 +1,7 @@ !!! 5 %html{ lang: "en"} = render "layouts/head", title: "Error" - %body{class: "#{app_theme} application"} + %body{class: "#{app_theme} #{theme_type} application"} = render "layouts/head_panel", title: "" if current_user .container.navless-container = render "layouts/flash" diff --git a/app/views/layouts/explore.html.haml b/app/views/layouts/explore.html.haml index d023846c5eb..9813d846542 100644 --- a/app/views/layouts/explore.html.haml +++ b/app/views/layouts/explore.html.haml @@ -2,7 +2,7 @@ !!! 5 %html{ lang: "en"} = render "layouts/head", title: page_title - %body{class: "#{app_theme} application", :'data-page' => body_data_page} + %body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page} = render "layouts/broadcast" - if current_user = render "layouts/head_panel", title: page_title diff --git a/app/views/layouts/group.html.haml b/app/views/layouts/group.html.haml index f22fb236cb5..72b0d03908d 100644 --- a/app/views/layouts/group.html.haml +++ b/app/views/layouts/group.html.haml @@ -1,12 +1,7 @@ !!! 5 %html{ lang: "en"} = render "layouts/head", title: group_head_title - %body{class: "#{app_theme} application", :'data-page' => body_data_page} + %body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page} = render "layouts/broadcast" - = render "layouts/head_panel", title: "group: #{@group.name}" - %nav.main-nav.navbar-collapse.collapse - .container= render 'layouts/nav/group' - .container - .content - = render "layouts/flash" - = yield + = render "layouts/head_panel", title: @group.name + = render 'layouts/page', sidebar: 'layouts/nav/group' diff --git a/app/views/layouts/nav/_admin.html.haml b/app/views/layouts/nav/_admin.html.haml index c57216f01c8..ea503a9cc2e 100644 --- a/app/views/layouts/nav/_admin.html.haml +++ b/app/views/layouts/nav/_admin.html.haml @@ -1,19 +1,42 @@ -%ul +%ul.nav.nav-sidebar = nav_link(controller: :dashboard, html_options: {class: 'home'}) do = link_to admin_root_path, title: "Stats" do - Overview + %i.fa.fa-dashboard + %span + Overview = nav_link(controller: :projects) do - = link_to "Projects", admin_projects_path + = link_to admin_projects_path do + %i.fa.fa-cube + %span + Projects = nav_link(controller: :users) do - = link_to "Users", admin_users_path + = link_to admin_users_path do + %i.fa.fa-users + %span + Users = nav_link(controller: :groups) do - = link_to "Groups", admin_groups_path + = link_to admin_groups_path do + %i.fa.fa-group + %span + Groups = nav_link(controller: :logs) do - = link_to "Logs", admin_logs_path + = link_to admin_logs_path do + %i.fa.fa-file-text + %span + Logs = nav_link(controller: :broadcast_messages) do - = link_to "Messages", admin_broadcast_messages_path + = link_to admin_broadcast_messages_path do + %i.fa.fa-bullhorn + %span + Messages = nav_link(controller: :hooks) do - = link_to "Hooks", admin_hooks_path + = link_to admin_hooks_path do + %i.fa.fa-external-link + %span + Hooks = nav_link(controller: :background_jobs) do - = link_to "Background Jobs", admin_background_jobs_path + = link_to admin_background_jobs_path do + %i.fa.fa-cog + %span + Background Jobs diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml index a6e9772d93f..a2eaa2d83c5 100644 --- a/app/views/layouts/nav/_dashboard.html.haml +++ b/app/views/layouts/nav/_dashboard.html.haml @@ -1,18 +1,29 @@ -%ul +%ul.nav.nav-sidebar = nav_link(path: 'dashboard#show', html_options: {class: 'home'}) do = link_to root_path, title: 'Home', class: 'shortcuts-activity' do - Activity + %i.fa.fa-dashboard + %span + Activity = nav_link(path: 'dashboard#projects') do = link_to projects_dashboard_path, class: 'shortcuts-projects' do - Projects + %i.fa.fa-cube + %span + Projects = nav_link(path: 'dashboard#issues') do - = link_to issues_dashboard_path, class: 'shortcuts-issues' do - Issues - %span.count= current_user.assigned_issues.opened.count + = link_to assigned_issues_dashboard_path, class: 'shortcuts-issues' do + %i.fa.fa-exclamation-circle + %span + Issues + %span.count= current_user.assigned_issues.opened.count = nav_link(path: 'dashboard#merge_requests') do - = link_to merge_requests_dashboard_path, class: 'shortcuts-merge_requests' do - Merge Requests - %span.count= current_user.assigned_merge_requests.opened.count + = link_to assigned_mrs_dashboard_path, class: 'shortcuts-merge_requests' do + %i.fa.fa-tasks + %span + Merge Requests + %span.count= current_user.assigned_merge_requests.opened.count = nav_link(controller: :help) do - = link_to "Help", help_path + = link_to help_path do + %i.fa.fa-question-circle + %span + Help diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml index 9095a843c9f..54468d077ab 100644 --- a/app/views/layouts/nav/_group.html.haml +++ b/app/views/layouts/nav/_group.html.haml @@ -1,25 +1,42 @@ -%ul +%ul.nav.nav-sidebar = nav_link(path: 'groups#show', html_options: {class: 'home'}) do = link_to group_path(@group), title: "Home" do - Activity - = nav_link(controller: [:group, :milestones]) do - = link_to group_milestones_path(@group) do - Milestones + %i.fa.fa-dashboard + %span + Activity + - if current_user + = nav_link(controller: [:group, :milestones]) do + = link_to group_milestones_path(@group) do + %i.fa.fa-clock-o + %span + Milestones = nav_link(path: 'groups#issues') do = link_to issues_group_path(@group) do - Issues - - if current_user - %span.count= current_user.assigned_issues.opened.of_group(@group).count + %i.fa.fa-exclamation-circle + %span + Issues + - if current_user + %span.count= Issue.opened.of_group(@group).count = nav_link(path: 'groups#merge_requests') do = link_to merge_requests_group_path(@group) do - Merge Requests - - if current_user - %span.count= current_user.cared_merge_requests.opened.of_group(@group).count + %i.fa.fa-tasks + %span + Merge Requests + - if current_user + %span.count= MergeRequest.opened.of_group(@group).count = nav_link(path: 'groups#members') do - = link_to "Members", members_group_path(@group) + = link_to members_group_path(@group) do + %i.fa.fa-users + %span + Members - if can?(current_user, :manage_group, @group) - = nav_link(path: 'groups#edit') do - = link_to edit_group_path(@group), class: "tab " do - Settings + = nav_link(html_options: { class: "#{"active" if group_settings_page?} separate-item" }) do + = link_to edit_group_path(@group), class: "tab no-highlight" do + %i.fa.fa-cogs + %span + Settings + %i.fa.fa-angle-down + - if group_settings_page? + = render 'groups/settings_nav' diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml index 1de5ee99cf4..36b48a5d02d 100644 --- a/app/views/layouts/nav/_profile.html.haml +++ b/app/views/layouts/nav/_profile.html.haml @@ -1,26 +1,55 @@ -%ul +%ul.nav.nav-sidebar = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do = link_to profile_path, title: "Profile" do - Profile + %i.fa.fa-user + %span + Profile = nav_link(controller: :accounts) do - = link_to "Account", profile_account_path + = link_to profile_account_path do + %i.fa.fa-gear + Account + = nav_link(path: ['profiles#applications', 'applications#edit', 'applications#show', 'applications#new']) do + = link_to applications_profile_path do + %i.fa.fa-cloud + %span + Applications = nav_link(controller: :emails) do = link_to profile_emails_path do - Emails - %span.count= current_user.emails.count + 1 + %i.fa.fa-envelope-o + %span + Emails + %span.count= current_user.emails.count + 1 - unless current_user.ldap_user? = nav_link(controller: :passwords) do - = link_to "Password", edit_profile_password_path + = link_to edit_profile_password_path do + %i.fa.fa-lock + %span + Password = nav_link(controller: :notifications) do - = link_to "Notifications", profile_notifications_path + = link_to profile_notifications_path do + %i.fa.fa-inbox + %span + Notifications + = nav_link(controller: :keys) do = link_to profile_keys_path do - SSH Keys - %span.count= current_user.keys.count + %i.fa.fa-key + %span + SSH Keys + %span.count= current_user.keys.count = nav_link(path: 'profiles#design') do - = link_to "Design", design_profile_path + = link_to design_profile_path do + %i.fa.fa-image + %span + Design = nav_link(controller: :groups) do - = link_to "Groups", profile_groups_path + = link_to profile_groups_path do + %i.fa.fa-group + %span + Groups = nav_link(path: 'profiles#history') do - = link_to "History", history_profile_path + = link_to history_profile_path do + %i.fa.fa-history + %span + History diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index 6cb2a82bac8..94cee0bd50f 100644 --- a/app/views/layouts/nav/_project.html.haml +++ b/app/views/layouts/nav/_project.html.haml @@ -1,45 +1,76 @@ -%ul.project-navigation +%ul.project-navigation.nav.nav-sidebar = nav_link(path: 'projects#show', html_options: {class: "home"}) do = link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do - Project + %i.fa.fa-dashboard + %span + Project - if project_nav_tab? :files = nav_link(controller: %w(tree blob blame edit_tree new_tree)) do - = link_to 'Files', project_tree_path(@project, @ref || @repository.root_ref), class: 'shortcuts-tree' + = link_to project_tree_path(@project, @ref || @repository.root_ref), class: 'shortcuts-tree' do + %i.fa.fa-files-o + %span + Files + - if project_nav_tab? :commits = nav_link(controller: %w(commit commits compare repositories tags branches)) do - = link_to "Commits", project_commits_path(@project, @ref || @repository.root_ref), class: 'shortcuts-commits' + = link_to project_commits_path(@project, @ref || @repository.root_ref), class: 'shortcuts-commits' do + %i.fa.fa-history + %span + Commits - if project_nav_tab? :network = nav_link(controller: %w(network)) do - = link_to "Network", project_network_path(@project, @ref || @repository.root_ref), class: 'shortcuts-network' + = link_to project_network_path(@project, @ref || @repository.root_ref), class: 'shortcuts-network' do + %i.fa.fa-code-fork + %span + Network - if project_nav_tab? :graphs = nav_link(controller: %w(graphs)) do - = link_to "Graphs", project_graph_path(@project, @ref || @repository.root_ref), class: 'shortcuts-graphs' + = link_to project_graph_path(@project, @ref || @repository.root_ref), class: 'shortcuts-graphs' do + %i.fa.fa-area-chart + %span + Graphs - if project_nav_tab? :issues = nav_link(controller: %w(issues milestones labels)) do = link_to url_for_project_issues, class: 'shortcuts-issues' do - Issues - - if @project.used_default_issues_tracker? - %span.count.issue_counter= @project.issues.opened.count + %i.fa.fa-exclamation-circle + %span + Issues + - if @project.used_default_issues_tracker? + %span.count.issue_counter= @project.issues.opened.count - if project_nav_tab? :merge_requests = nav_link(controller: :merge_requests) do = link_to project_merge_requests_path(@project), class: 'shortcuts-merge_requests' do - Merge Requests - %span.count.merge_counter= @project.merge_requests.opened.count + %i.fa.fa-tasks + %span + Merge Requests + %span.count.merge_counter= @project.merge_requests.opened.count - if project_nav_tab? :wiki = nav_link(controller: :wikis) do - = link_to 'Wiki', project_wiki_path(@project, :home), class: 'shortcuts-wiki' + = link_to project_wiki_path(@project, :home), class: 'shortcuts-wiki' do + %i.fa.fa-book + %span + Wiki - if project_nav_tab? :snippets = nav_link(controller: :snippets) do - = link_to 'Snippets', project_snippets_path(@project), class: 'shortcuts-snippets' + = link_to project_snippets_path(@project), class: 'shortcuts-snippets' do + %i.fa.fa-file-text-o + %span + Snippets - if project_nav_tab? :settings - = nav_link(html_options: {class: "#{project_tab_class}"}) do - = link_to edit_project_path(@project), class: "stat-tab tab " do - Settings + = nav_link(html_options: {class: "#{project_tab_class} separate-item"}) do + = link_to edit_project_path(@project), class: "stat-tab tab no-highlight" do + %i.fa.fa-cogs + %span + Settings + %i.fa.fa-angle-down + + - if @project_settings_nav + = render 'projects/settings_nav' diff --git a/app/views/layouts/navless.html.haml b/app/views/layouts/navless.html.haml index 2c5fffe384f..730f3d09277 100644 --- a/app/views/layouts/navless.html.haml +++ b/app/views/layouts/navless.html.haml @@ -1,7 +1,7 @@ !!! 5 %html{ lang: "en"} = render "layouts/head", title: @title - %body{class: "#{app_theme} application", :'data-page' => body_data_page} + %body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page} = render "layouts/broadcast" = render "layouts/head_panel", title: @title .container.navless-container diff --git a/app/views/layouts/profile.html.haml b/app/views/layouts/profile.html.haml index 1d0ab84d26f..941084cc4ad 100644 --- a/app/views/layouts/profile.html.haml +++ b/app/views/layouts/profile.html.haml @@ -1,12 +1,7 @@ !!! 5 %html{ lang: "en"} = render "layouts/head", title: "Profile" - %body{class: "#{app_theme} profile", :'data-page' => body_data_page} + %body{class: "#{app_theme} #{theme_type} profile", :'data-page' => body_data_page} = render "layouts/broadcast" = render "layouts/head_panel", title: "Profile" - %nav.main-nav.navbar-collapse.collapse - .container= render 'layouts/nav/profile' - .container - .content - = render "layouts/flash" - = yield + = render 'layouts/page', sidebar: 'layouts/nav/profile' diff --git a/app/views/layouts/project_settings.html.haml b/app/views/layouts/project_settings.html.haml index c8b8f4ba971..0f20bf38bfd 100644 --- a/app/views/layouts/project_settings.html.haml +++ b/app/views/layouts/project_settings.html.haml @@ -1,19 +1,9 @@ !!! 5 %html{ lang: "en"} = render "layouts/head", title: @project.name_with_namespace - %body{class: "#{app_theme} project", :'data-page' => body_data_page, :'data-project-id' => @project.id } + %body{class: "#{app_theme} #{theme_type} project", :'data-page' => body_data_page, :'data-project-id' => @project.id } = render "layouts/broadcast" = render "layouts/head_panel", title: project_title(@project) = render "layouts/init_auto_complete" - - if can?(current_user, :download_code, @project) - = render 'shared/no_ssh' - %nav.main-nav.navbar-collapse.collapse - .container= render 'layouts/nav/project' - .container - .content - = render "layouts/flash" - .row - .col-md-2 - = render "projects/settings_nav" - .col-md-10 - = yield + - @project_settings_nav = true + = render 'layouts/page', sidebar: 'layouts/nav/project' diff --git a/app/views/layouts/projects.html.haml b/app/views/layouts/projects.html.haml index 8ad2f165946..d4ee53db55c 100644 --- a/app/views/layouts/projects.html.haml +++ b/app/views/layouts/projects.html.haml @@ -1,16 +1,8 @@ !!! 5 %html{ lang: "en"} = render "layouts/head", title: project_head_title - %body{class: "#{app_theme} project", :'data-page' => body_data_page, :'data-project-id' => @project.id } + %body{class: "#{app_theme} #{theme_type} project", :'data-page' => body_data_page, :'data-project-id' => @project.id } = render "layouts/broadcast" = render "layouts/head_panel", title: project_title(@project) = render "layouts/init_auto_complete" - - if can?(current_user, :download_code, @project) - = render 'shared/no_ssh' - %nav.main-nav.navbar-collapse.collapse - .container= render 'layouts/nav/project' - .container - .content - = render "layouts/flash" - = yield - = yield :embedded_scripts + = render 'layouts/page', sidebar: 'layouts/nav/project' diff --git a/app/views/layouts/public_group.html.haml b/app/views/layouts/public_group.html.haml index a289b784725..64794104ac5 100644 --- a/app/views/layouts/public_group.html.haml +++ b/app/views/layouts/public_group.html.haml @@ -1,10 +1,7 @@ !!! 5 %html{ lang: "en"} = render "layouts/head", title: group_head_title - %body{class: "#{app_theme} application", :'data-page' => body_data_page} + %body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page} = render "layouts/broadcast" = render "layouts/public_head_panel", title: "group: #{@group.name}" - %nav.main-nav.navbar-collapse.collapse - .container= render 'layouts/nav/group' - .container - .content= yield + = render 'layouts/page', sidebar: 'layouts/nav/group' diff --git a/app/views/layouts/public_projects.html.haml b/app/views/layouts/public_projects.html.haml index 2a9230244f8..5964a29d522 100644 --- a/app/views/layouts/public_projects.html.haml +++ b/app/views/layouts/public_projects.html.haml @@ -1,10 +1,7 @@ !!! 5 %html{ lang: "en"} = render "layouts/head", title: @project.name_with_namespace - %body{class: "#{app_theme} application", :'data-page' => body_data_page} + %body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page} = render "layouts/broadcast" = render "layouts/public_head_panel", title: project_title(@project) - %nav.main-nav.navbar-collapse.collapse - .container= render 'layouts/nav/project' - .container - .content= yield + = render 'layouts/page', sidebar: 'layouts/nav/project' diff --git a/app/views/layouts/public_users.html.haml b/app/views/layouts/public_users.html.haml index 4aa258fea0d..0510ce34a7f 100644 --- a/app/views/layouts/public_users.html.haml +++ b/app/views/layouts/public_users.html.haml @@ -1,8 +1,7 @@ !!! 5 %html{ lang: "en"} = render "layouts/head", title: @title - %body{class: "#{app_theme} application", :'data-page' => body_data_page} + %body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page} = render "layouts/broadcast" = render "layouts/public_head_panel", title: @title - .container.navless-container - .content= yield + = render 'layouts/page' diff --git a/app/views/layouts/search.html.haml b/app/views/layouts/search.html.haml index 084ff7ec830..6d001e7ee1c 100644 --- a/app/views/layouts/search.html.haml +++ b/app/views/layouts/search.html.haml @@ -1,7 +1,7 @@ !!! 5 %html{ lang: "en"} = render "layouts/head", title: "Search" - %body{class: "#{app_theme} application", :'data-page' => body_data_page} + %body{class: "#{app_theme} #{theme_type} application", :'data-page' => body_data_page} = render "layouts/broadcast" = render "layouts/head_panel", title: "Search" .container.navless-container diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml index a21dcff41c0..53a50f6796b 100644 --- a/app/views/profiles/accounts/show.html.haml +++ b/app/views/profiles/accounts/show.html.haml @@ -75,3 +75,4 @@ The following 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, data: { confirm: "REMOVE #{current_user.name}? Are you sure?" }, method: :delete, class: "btn btn-remove" + diff --git a/app/views/profiles/applications.html.haml b/app/views/profiles/applications.html.haml new file mode 100644 index 00000000000..cb24e4a3dde --- /dev/null +++ b/app/views/profiles/applications.html.haml @@ -0,0 +1,47 @@ +%h3.page-title + OAuth2 + +%fieldset.oauth-applications + %legend Your applications + %p= link_to 'New Application', new_oauth_application_path, class: 'btn btn-success' + - if @applications.any? + %table.table.table-striped + %thead + %tr + %th Name + %th Callback URL + %th Clients + %th + %th + %tbody + - @applications.each do |application| + %tr{:id => "application_#{application.id}"} + %td= link_to application.name, oauth_application_path(application) + %td + - application.redirect_uri.split.each do |uri| + %div= uri + %td= application.access_tokens.count + %td= link_to 'Edit', edit_oauth_application_path(application), class: 'btn btn-link btn-small' + %td= render 'doorkeeper/applications/delete_form', application: application + +%fieldset.oauth-authorized-applications.prepend-top-20 + %legend Authorized applications + + - if @authorized_tokens.any? + %table.table.table-striped + %thead + %tr + %th Name + %th Authorized At + %th Scope + %th + %tbody + - @authorized_tokens.each do |token| + - application = token.application + %tr{:id => "application_#{application.id}"} + %td= application.name + %td= token.created_at + %td= token.scopes + %td= render 'doorkeeper/authorized_applications/delete_form', application: application + - else + %p.light You dont have any authorized applications diff --git a/app/views/profiles/update.js.erb b/app/views/profiles/update.js.erb index 04b5cf4827d..e664ac2a52a 100644 --- a/app/views/profiles/update.js.erb +++ b/app/views/profiles/update.js.erb @@ -1,6 +1,6 @@ // Remove body class for any previous theme, re-add current one -$('body').removeClass('ui_basic ui_mars ui_modern ui_gray ui_color') -$('body').addClass('<%= app_theme %>') +$('body').removeClass('ui_basic ui_mars ui_modern ui_gray ui_color light_theme dark_theme') +$('body').addClass('<%= app_theme %> <%= theme_type %>') // Re-render the header to reflect the new theme $('header').html('<%= escape_javascript(render("layouts/head_panel", title: "Profile")) %>') diff --git a/app/views/projects/_issuable_filter.html.haml b/app/views/projects/_issuable_filter.html.haml deleted file mode 100644 index b3e5efd938f..00000000000 --- a/app/views/projects/_issuable_filter.html.haml +++ /dev/null @@ -1,72 +0,0 @@ -.issues-filters - .dropdown.inline - %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} - %i.fa.fa-user - %span.light assignee: - - if @assignee.present? - %strong= @assignee.name - - elsif params[:assignee_id] == "0" - Unassigned - - else - Any - %b.caret - %ul.dropdown-menu - %li - = link_to project_filter_path(assignee_id: nil) do - Any - = link_to project_filter_path(assignee_id: 0) do - Unassigned - - @assignees.sort_by(&:name).each do |user| - %li - = link_to project_filter_path(assignee_id: user.id) do - = image_tag avatar_icon(user.email), class: "avatar s16", alt: '' - = user.name - - .dropdown.inline.prepend-left-10 - %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} - %i.fa.fa-user - %span.light author: - - if @author.present? - %strong= @author.name - - elsif params[:author_id] == "0" - Unassigned - - else - Any - %b.caret - %ul.dropdown-menu - %li - = link_to project_filter_path(author_id: nil) do - Any - = link_to project_filter_path(author_id: 0) do - Unassigned - - @authors.sort_by(&:name).each do |user| - %li - = link_to project_filter_path(author_id: user.id) do - = image_tag avatar_icon(user.email), class: "avatar s16", alt: '' - = user.name - - .dropdown.inline.prepend-left-10 - %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} - %i.fa.fa-clock-o - %span.light milestone: - - if @milestone.present? - %strong= @milestone.title - - elsif params[:milestone_id] == "0" - None (backlog) - - else - Any - %b.caret - %ul.dropdown-menu - %li - = link_to project_filter_path(milestone_id: nil) do - Any - = link_to project_filter_path(milestone_id: 0) do - None (backlog) - - project_active_milestones.each do |milestone| - %li - = link_to project_filter_path(milestone_id: milestone.id) do - %strong= milestone.title - %small.light= milestone.expires_at - - .pull-right - = render 'shared/sort_dropdown' diff --git a/app/views/projects/_issues_nav.html.haml b/app/views/projects/_issues_nav.html.haml index 18628eb6207..4e2ef3202f9 100644 --- a/app/views/projects/_issues_nav.html.haml +++ b/app/views/projects/_issues_nav.html.haml @@ -2,15 +2,22 @@ - if project_nav_tab? :issues = nav_link(controller: :issues) do = link_to project_issues_path(@project), class: "tab" do + %i.fa.fa-exclamation-circle Issues - if project_nav_tab? :merge_requests = nav_link(controller: :merge_requests) do = link_to project_merge_requests_path(@project), class: "tab" do + %i.fa.fa-tasks Merge Requests = nav_link(controller: :milestones) do - = link_to 'Milestones', project_milestones_path(@project), class: "tab" + = link_to project_milestones_path(@project), class: "tab" do + %i.fa.fa-clock-o + Milestones = nav_link(controller: :labels) do - = link_to 'Labels', project_labels_path(@project), class: "tab" + = link_to project_labels_path(@project), class: "tab" do + %i.fa.fa-tags + Labels + - if current_controller?(:milestones) %li.pull-right diff --git a/app/views/projects/_settings_nav.html.haml b/app/views/projects/_settings_nav.html.haml index 2008f8c558d..64eda0bf286 100644 --- a/app/views/projects/_settings_nav.html.haml +++ b/app/views/projects/_settings_nav.html.haml @@ -1,25 +1,31 @@ -%ul.nav.nav-pills.nav-stacked.nav-stacked-menu.append-bottom-20.project-settings-nav +%ul.project-settings-nav.sidebar-subnav = nav_link(path: 'projects#edit') do = link_to edit_project_path(@project), class: "stat-tab tab " do %i.fa.fa-pencil-square-o - Project + %span + Project = nav_link(controller: [:team_members, :teams]) do = link_to project_team_index_path(@project), class: "team-tab tab" do %i.fa.fa-users - Members + %span + Members = nav_link(controller: :deploy_keys) do = link_to project_deploy_keys_path(@project) do %i.fa.fa-key - Deploy Keys + %span + Deploy Keys = nav_link(controller: :hooks) do = link_to project_hooks_path(@project) do %i.fa.fa-link - Web Hooks + %span + Web Hooks = nav_link(controller: :services) do = link_to project_services_path(@project) do %i.fa.fa-cogs - Services + %span + Services = nav_link(controller: :protected_branches) do = link_to project_protected_branches_path(@project) do %i.fa.fa-lock - Protected branches + %span + Protected branches diff --git a/app/views/projects/blob/_actions.html.haml b/app/views/projects/blob/_actions.html.haml index 812d88a8730..f428ae41ef4 100644 --- a/app/views/projects/blob/_actions.html.haml +++ b/app/views/projects/blob/_actions.html.haml @@ -1,11 +1,5 @@ .btn-group.tree-btn-group - -# only show edit link for text files - - if @blob.text? - - if allowed_tree_edit? - = link_to 'Edit', project_edit_tree_path(@project, @id), - class: 'btn btn-small' - - else - %span.btn.btn-small.disabled Edit + = edit_blob_link(@project, @ref, @path) = link_to 'Raw', project_raw_path(@project, @id), class: 'btn btn-small', target: '_blank' -# only show normal/blame view links for text files diff --git a/app/views/projects/blob/_blob.html.haml b/app/views/projects/blob/_blob.html.haml index 492dff437b5..68f3b08b8c8 100644 --- a/app/views/projects/blob/_blob.html.haml +++ b/app/views/projects/blob/_blob.html.haml @@ -16,7 +16,7 @@ = link_to title, '#' %ul.blob-commit-info.bs-callout.bs-callout-info.hidden-xs - - blob_commit = @repository.last_commit_for_path(@commit.id, @blob.path) + - blob_commit = @repository.last_commit_for_path(@commit.id, blob.path) = render blob_commit, project: @project %div#tree-content-holder.tree-content-holder diff --git a/app/views/projects/diffs/_diffs.html.haml b/app/views/projects/diffs/_diffs.html.haml index 334ea1ba82f..48d4c33ce85 100644 --- a/app/views/projects/diffs/_diffs.html.haml +++ b/app/views/projects/diffs/_diffs.html.haml @@ -2,15 +2,9 @@ .col-md-8 = render 'projects/diffs/stats', diffs: diffs .col-md-4 - %ul.nav.nav-tabs - %li.pull-right{class: params[:view] == 'parallel' ? 'active' : ''} - - params_copy = params.dup - - params_copy[:view] = 'parallel' - = link_to "Side-by-side Diff", url_for(params_copy), {id: "commit-diff-viewtype"} - %li.pull-right{class: params[:view] != 'parallel' ? 'active' : ''} - - params_copy[:view] = 'inline' - = link_to "Inline Diff", url_for(params_copy), {id: "commit-diff-viewtype"} - + .btn-group.pull-right + = inline_diff_btn + = parallel_diff_btn - if show_diff_size_warning?(diffs) = render 'projects/diffs/warning', diffs: diffs diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml index bf7770ceedf..23e7691b32e 100644 --- a/app/views/projects/diffs/_file.html.haml +++ b/app/views/projects/diffs/_file.html.haml @@ -30,9 +30,9 @@ - if @merge_request && @merge_request.source_project - = link_to project_edit_tree_path(@merge_request.source_project, tree_join(@merge_request.source_branch, diff_file.new_path), from_merge_request_id: @merge_request.id), { class: 'btn btn-small' } do - Edit - + = edit_blob_link(@merge_request.source_project, + @merge_request.source_branch, diff_file.new_path, + after: ' ', from_merge_request_id: @merge_request.id) = view_file_btn(@commit.id, diff_file, project) diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index b85cf7d8d37..f2bb56b5664 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -136,6 +136,8 @@ .col-sm-9 .form-group .input-group + .input-group-addon + #{URI.join(root_url, @project.namespace.path)}/ = f.text_field :path, class: 'form-control' %span.input-group-addon .git %ul diff --git a/app/views/projects/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml new file mode 100644 index 00000000000..ec03f375d6b --- /dev/null +++ b/app/views/projects/issues/_discussion.html.haml @@ -0,0 +1,37 @@ +- content_for :note_actions do + - if can?(current_user, :modify_issue, @issue) + - if @issue.closed? + = link_to 'Reopen Issue', project_issue_path(@project, @issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn btn-grouped btn-reopen js-note-target-reopen", title: 'Reopen Issue' + - else + = link_to 'Close Issue', project_issue_path(@project, @issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn btn-grouped btn-close js-note-target-close", title: "Close Issue" +.row + .col-md-9 + .participants + %cite.cgray + = pluralize(@issue.participants.count, 'participant') + - @issue.participants.each do |participant| + = link_to_member(@project, participant, name: false, size: 24) + + .voting_notes#notes= render "projects/notes/notes_with_form" + .col-md-3.hidden-sm.hidden-xs + %div + .clearfix + %span.slead.has_tooltip{:"data-original-title" => 'Cross-project reference'} + = cross_project_reference(@project, @issue) + %hr + .clearfix + .votes-holder + %h6 Votes + #votes= render 'votes/votes_block', votable: @issue + %hr + .context + %cite.cgray + = render partial: 'issue_context', locals: { issue: @issue } + + - if @issue.labels.any? + %hr + %h6 Labels + .issue-show-labels + - @issue.labels.each do |label| + = link_to project_issues_path(@project, label_name: label.name) do + %p= render_colored_label(label) diff --git a/app/views/projects/issues/_issue_context.html.haml b/app/views/projects/issues/_issue_context.html.haml index 648f459dc9e..98777a58f9d 100644 --- a/app/views/projects/issues/_issue_context.html.haml +++ b/app/views/projects/issues/_issue_context.html.haml @@ -1,25 +1,24 @@ = form_for [@project, @issue], remote: true, html: {class: 'edit-issue inline-update'} do |f| - .row - .col-sm-6 - %strong.append-right-10 - Assignee: + %div.prepend-top-20 + %p + Assignee: - - if can?(current_user, :modify_issue, @issue) - = project_users_select_tag('issue[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @issue.assignee_id) - - elsif issue.assignee - = link_to_member(@project, @issue.assignee) - - else - None + - if can?(current_user, :modify_issue, @issue) + = project_users_select_tag('issue[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @issue.assignee_id) + - elsif issue.assignee + = link_to_member(@project, @issue.assignee) + - else + None - .col-sm-6.text-right - %strong.append-right-10 - Milestone: - - if can?(current_user, :modify_issue, @issue) - = f.select(:milestone_id, milestone_options(@issue), { include_blank: "Select milestone" }, {class: 'select2 select2-compact js-select2 js-milestone'}) - = hidden_field_tag :issue_context - = f.submit class: 'btn' - - elsif issue.milestone - = link_to project_milestone_path(@project, @issue.milestone) do - = @issue.milestone.title - - else - None + %div.prepend-top-20 + %p + Milestone: + - if can?(current_user, :modify_issue, @issue) + = f.select(:milestone_id, milestone_options(@issue), { include_blank: "Select milestone" }, {class: 'select2 select2-compact js-select2 js-milestone'}) + = hidden_field_tag :issue_context + = f.submit class: 'btn' + - elsif issue.milestone + = link_to project_milestone_path(@project, @issue.milestone) do + = @issue.milestone.title + - else + None diff --git a/app/views/projects/issues/_issues.html.haml b/app/views/projects/issues/_issues.html.haml index 15c84c7ced2..010ca3b68b3 100644 --- a/app/views/projects/issues/_issues.html.haml +++ b/app/views/projects/issues/_issues.html.haml @@ -1,7 +1,7 @@ .append-bottom-10 .check-all-holder = check_box_tag "check_all_issues", nil, false, class: "check_all_issues left" - = render 'projects/issuable_filter' + = render 'shared/issuable_filter' .clearfix .issues_bulk_update.hide diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml index 8db6241f21f..0d00d6bfded 100644 --- a/app/views/projects/issues/index.html.haml +++ b/app/views/projects/issues/index.html.haml @@ -1,9 +1,4 @@ = render "projects/issues_nav" -.row - .fixed.fixed.sidebar-expand-button.hidden-lg.hidden-md.hidden-xs - %i.fa.fa-list.fa-2x - .col-md-3.responsive-side - = render 'shared/project_filter', project_entities_path: project_issues_path(@project), - labels: true, redirect: 'issues', entity: 'issue' - .col-md-9.issues-holder - = render "issues" + +.issues-holder + = render "issues" diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index 01a1fabda26..b21a394ebeb 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -1,7 +1,14 @@ -%h3.page-title +%h4.page-title + .issue-box{ class: issue_box_class(@issue) } + - if @issue.closed? + Closed + - else + Open Issue ##{@issue.iid} + %small.creator + · created by #{link_to_member(@project, @issue.author)} #{issue_timestamp(@issue)} - %span.pull-right.issue-btn-group + .pull-right - if can?(current_user, :write_issue, @project) = link_to new_project_issue_path(@project), class: "btn btn-grouped", title: "New Issue", id: "new_issue_link" do %i.fa.fa-plus @@ -12,68 +19,19 @@ - else = link_to 'Close', project_issue_path(@project, @issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn btn-grouped btn-close", title: "Close Issue" - = link_to edit_project_issue_path(@project, @issue), class: "btn btn-grouped" do + = link_to edit_project_issue_path(@project, @issue), class: "btn btn-grouped issuable-edit" do %i.fa.fa-pencil-square-o Edit -.clearfix - .votes-holder - #votes= render 'votes/votes_block', votable: @issue - - .back-link - = link_to project_issues_path(@project) do - ← To issues list - %span.milestone-nav-link - - if @issue.milestone - | - %span.light Milestone - = link_to project_milestone_path(@project, @issue.milestone) do - = @issue.milestone.title - -.issue-box{ class: issue_box_class(@issue) } - .state.clearfix - .state-label - - if @issue.closed? - Closed - - else - Open - - .cross-project-ref - %i.fa.fa-link.has_tooltip{:"data-original-title" => 'Cross-project reference'} - = cross_project_reference(@project, @issue) - - .creator - Created by #{link_to_member(@project, @issue.author)} #{issue_timestamp(@issue)} - - %h4.title - = gfm escape_once(@issue.title) - +%hr +%h3.issue-title + = gfm escape_once(@issue.title) +%div - if @issue.description.present? .description .wiki = preserve do = markdown(@issue.description, parse_tasks: true) - .context - %cite.cgray - = render partial: 'issue_context', locals: { issue: @issue } - - -- content_for :note_actions do - - if can?(current_user, :modify_issue, @issue) - - if @issue.closed? - = link_to 'Reopen Issue', project_issue_path(@project, @issue, issue: {state_event: :reopen }, status_only: true), method: :put, class: "btn btn-grouped btn-reopen js-note-target-reopen", title: 'Reopen Issue' - - else - = link_to 'Close Issue', project_issue_path(@project, @issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn btn-grouped btn-close js-note-target-close", title: "Close Issue" - -.participants - %cite.cgray - = pluralize(@issue.participants.count, 'participant') - - @issue.participants.each do |participant| - = link_to_member(@project, participant, name: false, size: 24) - - .issue-show-labels.pull-right - - @issue.labels.each do |label| - = link_to project_issues_path(@project, label_name: label.name) do - = render_colored_label(label) -.voting_notes#notes= render "projects/notes/notes_with_form" +%hr += render "projects/issues/discussion" diff --git a/app/views/projects/issues/update.js.haml b/app/views/projects/issues/update.js.haml index 5199e9fc61f..6e50667b084 100644 --- a/app/views/projects/issues/update.js.haml +++ b/app/views/projects/issues/update.js.haml @@ -3,7 +3,7 @@ :plain $("##{dom_id(@issue)}").fadeOut(); - elsif params[:issue_context] - $('.issue-box .context').effect('highlight'); + $('.context').effect('highlight'); - if @issue.milestone $('.milestone-nav-link').replaceWith("<span class='milestone-nav-link'>| <span class='light'>Milestone</span> #{escape_javascript(link_to @issue.milestone.title, project_milestone_path(@issue.project, @issue.milestone))}</span>") - else diff --git a/app/views/projects/merge_requests/_discussion.html.haml b/app/views/projects/merge_requests/_discussion.html.haml new file mode 100644 index 00000000000..6bb5c465596 --- /dev/null +++ b/app/views/projects/merge_requests/_discussion.html.haml @@ -0,0 +1,31 @@ +- content_for :note_actions do + - if can?(current_user, :modify_merge_request, @merge_request) + - if @merge_request.open? + = link_to 'Close', project_merge_request_path(@project, @merge_request, merge_request: {state_event: :close }), method: :put, class: "btn btn-grouped btn-close close-mr-link js-note-target-close", title: "Close merge request" + - if @merge_request.closed? + = link_to 'Reopen', project_merge_request_path(@project, @merge_request, merge_request: {state_event: :reopen }), method: :put, class: "btn btn-grouped btn-reopen reopen-mr-link js-note-target-reopen", title: "Reopen merge request" + +.row + .col-md-9 + = render "projects/merge_requests/show/participants" + = render "projects/notes/notes_with_form" + .col-md-3.hidden-sm.hidden-xs + .clearfix + %span.slead.has_tooltip{:"data-original-title" => 'Cross-project reference'} + = cross_project_reference(@project, @merge_request) + %hr + .votes-holder.hidden-sm.hidden-xs + %h6 Votes + #votes= render 'votes/votes_block', votable: @merge_request + %hr + .context + %cite.cgray + = render partial: 'projects/merge_requests/show/context', locals: { merge_request: @merge_request } + + - if @merge_request.labels.any? + %hr + %h6 Labels + .merge-request-show-labels + - @merge_request.labels.each do |label| + = link_to project_merge_requests_path(@project, label_name: label.name) do + %p= render_colored_label(label) diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index 7b28dd5e7da..74ef819a7aa 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -1,39 +1,68 @@ .merge-request = render "projects/merge_requests/show/mr_title" - = render "projects/merge_requests/show/how_to_merge" + %hr = render "projects/merge_requests/show/mr_box" + %hr + .append-bottom-20 + .slead + %span From + - if @merge_request.for_fork? + %strong.label-branch< + - if @merge_request.source_project + = link_to @merge_request.source_project_namespace, project_path(@merge_request.source_project) + - else + \ #{@merge_request.source_project_namespace} + \:#{@merge_request.source_branch} + %span into + %strong.label-branch #{@merge_request.target_project_namespace}:#{@merge_request.target_branch} + - else + %strong.label-branch #{@merge_request.source_branch} + %span into + %strong.label-branch #{@merge_request.target_branch} + - if @merge_request.open? + %span.pull-right + .btn-group + %a.btn.dropdown-toggle{ data: {toggle: :dropdown} } + %i.fa.fa-download + Download as + %span.caret + %ul.dropdown-menu + %li= link_to "Email Patches", project_merge_request_path(@project, @merge_request, format: :patch) + %li= link_to "Plain Diff", project_merge_request_path(@project, @merge_request, format: :diff) + + = render "projects/merge_requests/show/how_to_merge" = render "projects/merge_requests/show/state_widget" - = render "projects/merge_requests/show/commits" - = render "projects/merge_requests/show/participants" - if @commits.present? - %ul.nav.nav-pills.merge-request-tabs + %ul.nav.nav-tabs.merge-request-tabs %li.notes-tab{data: {action: 'notes'}} = link_to project_merge_request_path(@project, @merge_request) do - %i.fa.fa-comment + %i.fa.fa-comments Discussion %span.badge= @merge_request.mr_and_commit_notes.count + %li.commits-tab{data: {action: 'commits'}} + = link_to project_merge_request_path(@project, @merge_request), title: 'Commits' do + %i.fa.fa-database + Commits + %span.badge= @commits.size %li.diffs-tab{data: {action: 'diffs'}} = link_to diffs_project_merge_request_path(@project, @merge_request) do %i.fa.fa-list-alt Changes %span.badge= @merge_request.diffs.size - - content_for :note_actions do - - if can?(current_user, :modify_merge_request, @merge_request) - - if @merge_request.open? - = link_to 'Close', project_merge_request_path(@project, @merge_request, merge_request: {state_event: :close }), method: :put, class: "btn btn-grouped btn-close close-mr-link js-note-target-close", title: "Close merge request" - - if @merge_request.closed? - = link_to 'Reopen', project_merge_request_path(@project, @merge_request, merge_request: {state_event: :reopen }), method: :put, class: "btn btn-grouped btn-reopen reopen-mr-link js-note-target-reopen", title: "Reopen merge request" - + .notes.tab-content.voting_notes#notes{ class: (controller.action_name == 'show') ? "" : "hide" } + = render "projects/merge_requests/discussion" + .commits.tab-content + = render "projects/merge_requests/show/commits" .diffs.tab-content - if current_page?(action: 'diffs') = render "projects/merge_requests/show/diffs" - .notes.tab-content.voting_notes#notes{ class: (controller.action_name == 'show') ? "" : "hide" } - = render "projects/notes/notes_with_form" + .mr-loading-status = spinner + :javascript var merge_request; diff --git a/app/views/projects/merge_requests/index.html.haml b/app/views/projects/merge_requests/index.html.haml index b93e0f9da3e..2654ea70990 100644 --- a/app/views/projects/merge_requests/index.html.haml +++ b/app/views/projects/merge_requests/index.html.haml @@ -1,23 +1,19 @@ = render "projects/issues_nav" -.row - .col-md-3.responsive-side - = render 'shared/project_filter', project_entities_path: project_merge_requests_path(@project), - labels: true, redirect: 'merge_requests', entity: 'merge_request' - .col-md-9 - .append-bottom-10 - = render 'projects/issuable_filter' - .panel.panel-default - %ul.well-list.mr-list - = render @merge_requests - - if @merge_requests.blank? - %li - .nothing-here-block No merge requests to show - - if @merge_requests.present? - .pull-right - %span.cgray.pull-right #{@merge_requests.total_count} merge requests for this filter +.merge-requests-holder + .append-bottom-10 + = render 'shared/issuable_filter' + .panel.panel-default + %ul.well-list.mr-list + = render @merge_requests + - if @merge_requests.blank? + %li + .nothing-here-block No merge requests to show + - if @merge_requests.present? + .pull-right + %span.cgray.pull-right #{@merge_requests.total_count} merge requests for this filter - = paginate @merge_requests, theme: "gitlab" + = paginate @merge_requests, theme: "gitlab" :javascript $(merge_requestsPage); diff --git a/app/views/projects/merge_requests/show/_context.html.haml b/app/views/projects/merge_requests/show/_context.html.haml index 089302e3588..5b6e64f0657 100644 --- a/app/views/projects/merge_requests/show/_context.html.haml +++ b/app/views/projects/merge_requests/show/_context.html.haml @@ -1,24 +1,23 @@ = form_for [@project, @merge_request], remote: true, html: {class: 'edit-merge_request inline-update'} do |f| - .row - .col-sm-6 - %strong.append-right-10 - Assignee: + %div.prepend-top-20 + %p + Assignee: - - if can?(current_user, :modify_merge_request, @merge_request) - = project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @merge_request.assignee_id) - - elsif merge_request.assignee - = link_to_member(@project, @merge_request.assignee) - - else - None + - if can?(current_user, :modify_merge_request, @merge_request) + = project_users_select_tag('merge_request[assignee_id]', placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: @merge_request.assignee_id) + - elsif merge_request.assignee + = link_to_member(@project, @merge_request.assignee) + - else + None - .col-sm-6.text-right - %strong.append-right-10 - Milestone: - - if can?(current_user, :modify_merge_request, @merge_request) - = f.select(:milestone_id, milestone_options(@merge_request), { include_blank: "Select milestone" }, {class: 'select2 select2-compact js-select2 js-milestone'}) - = hidden_field_tag :merge_request_context - = f.submit class: 'btn' - - elsif merge_request.milestone - = link_to merge_request.milestone.title, project_milestone_path - - else - None + %div.prepend-top-20 + %p + Milestone: + - if can?(current_user, :modify_merge_request, @merge_request) + = f.select(:milestone_id, milestone_options(@merge_request), { include_blank: "Select milestone" }, {class: 'select2 select2-compact js-select2 js-milestone'}) + = hidden_field_tag :merge_request_context + = f.submit class: 'btn' + - elsif merge_request.milestone + = link_to merge_request.milestone.title, project_milestone_path + - else + None 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 866b236d827..ab1284547ad 100644 --- a/app/views/projects/merge_requests/show/_mr_box.html.haml +++ b/app/views/projects/merge_requests/show/_mr_box.html.haml @@ -1,29 +1,9 @@ -.issue-box{ class: issue_box_class(@merge_request) } - .state.clearfix - .state-label - - if @merge_request.merged? - Merged - - elsif @merge_request.closed? - Closed - - else - Open - - .cross-project-ref - %i.fa.fa-link.has_tooltip{:"data-original-title" => 'Cross-project reference'} - = cross_project_reference(@project, @merge_request) - - .creator - Created by #{link_to_member(@project, @merge_request.author)} #{time_ago_with_tooltip(@merge_request.created_at)} - - %h4.title - = gfm escape_once(@merge_request.title) +%h3.issue-title + = gfm escape_once(@merge_request.title) +%div - if @merge_request.description.present? .description .wiki = preserve do = markdown(@merge_request.description, parse_tasks: true) - - .context - %cite.cgray - = render partial: 'projects/merge_requests/show/context', locals: { merge_request: @merge_request } 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 6fe765248e4..0f20eba382c 100644 --- a/app/views/projects/merge_requests/show/_mr_title.html.haml +++ b/app/views/projects/merge_requests/show/_mr_title.html.haml @@ -1,45 +1,22 @@ -%h3.page-title +%h4.page-title + .issue-box{ class: issue_box_class(@merge_request) } + - if @merge_request.merged? + Merged + - elsif @merge_request.closed? + Closed + - else + Open = "Merge Request ##{@merge_request.iid}" + %small.creator + · + created by #{link_to_member(@project, @merge_request.author)} #{time_ago_with_tooltip(@merge_request.created_at)} - %span.pull-right.issue-btn-group + .issue-btn-group.pull-right - if can?(current_user, :modify_merge_request, @merge_request) - if @merge_request.open? - .btn-group.pull-left - %a.btn.btn-grouped.dropdown-toggle{ data: {toggle: :dropdown} } - %i.fa.fa-download - Download as - %span.caret - %ul.dropdown-menu - %li= link_to "Email Patches", project_merge_request_path(@project, @merge_request, format: :patch) - %li= link_to "Plain Diff", project_merge_request_path(@project, @merge_request, format: :diff) - = link_to 'Close', project_merge_request_path(@project, @merge_request, merge_request: { state_event: :close }), method: :put, class: "btn btn-grouped btn-close", title: "Close merge request" - - = link_to edit_project_merge_request_path(@project, @merge_request), class: "btn btn-grouped", id:"edit_merge_request" do + = link_to edit_project_merge_request_path(@project, @merge_request), class: "btn btn-grouped issuable-edit", id: "edit_merge_request" do %i.fa.fa-pencil-square-o Edit - if @merge_request.closed? = link_to 'Reopen', project_merge_request_path(@project, @merge_request, merge_request: {state_event: :reopen }), method: :put, class: "btn btn-grouped btn-reopen reopen-mr-link", title: "Close merge request" - -.votes-holder.hidden-sm.hidden-xs - #votes= render 'votes/votes_block', votable: @merge_request - -.back-link - = link_to project_merge_requests_path(@project) do - ← To merge requests - - %span.prepend-left-20 - %span From - - if @merge_request.for_fork? - %strong.label-branch< - - if @merge_request.source_project - = link_to @merge_request.source_project_namespace, project_path(@merge_request.source_project) - - else - \ #{@merge_request.source_project_namespace} - \:#{@merge_request.source_branch} - %span into - %strong.label-branch #{@merge_request.target_project_namespace}:#{@merge_request.target_branch} - - else - %strong.label-branch #{@merge_request.source_branch} - %span into - %strong.label-branch #{@merge_request.target_branch} diff --git a/app/views/projects/merge_requests/show/_participants.html.haml b/app/views/projects/merge_requests/show/_participants.html.haml index b709c89cec2..15a97404cb0 100644 --- a/app/views/projects/merge_requests/show/_participants.html.haml +++ b/app/views/projects/merge_requests/show/_participants.html.haml @@ -2,8 +2,3 @@ %cite.cgray #{@merge_request.participants.count} participants - @merge_request.participants.each do |participant| = link_to_member(@project, participant, name: false, size: 24) - - .merge-request-show-labels.pull-right - - @merge_request.labels.each do |label| - = link_to project_merge_requests_path(@project, label_name: label.name) do - = render_colored_label(label) diff --git a/app/views/projects/merge_requests/update.js.haml b/app/views/projects/merge_requests/update.js.haml index 6452cc6382d..6f4c5dd7a3b 100644 --- a/app/views/projects/merge_requests/update.js.haml +++ b/app/views/projects/merge_requests/update.js.haml @@ -1,2 +1,2 @@ - if params[:merge_request_context] - $('.issue-box .context').effect('highlight'); + $('.context').effect('highlight'); diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index f08ccc1d570..031b5a31895 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -1,6 +1,15 @@ = render "projects/issues_nav" -%h3.page-title +%h4.page-title + .issue-box{ class: issue_box_class(@milestone) } + - if @milestone.closed? + Closed + - elsif @milestone.expired? + Expired + - else + Open Milestone ##{@milestone.iid} + %small.creator + = @milestone.expires_at .pull-right - if can?(current_user, :admin_milestone, @project) = link_to edit_project_milestone_path(@project, @milestone), class: "btn btn-grouped" do @@ -11,47 +20,32 @@ - else = link_to 'Reopen Milestone', project_milestone_path(@project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-grouped" +%hr - if @milestone.issues.any? && @milestone.can_be_closed? .alert.alert-success %span All issues for this milestone are closed. You may close milestone now. -.back-link - = link_to project_milestones_path(@project) do - ← To milestones list - - -.issue-box{ class: issue_box_class(@milestone) } - .state.clearfix - .state-label - - if @milestone.closed? - Closed - - elsif @milestone.expired? - Expired - - else - Open - .creator - = @milestone.expires_at - - %h4.title - = gfm escape_once(@milestone.title) - +%h3.issue-title + = gfm escape_once(@milestone.title) +%div - if @milestone.description.present? .description .wiki = preserve do = markdown @milestone.description - .context - %p - Progress: - #{@milestone.closed_items_count} closed - – - #{@milestone.open_items_count} open - - %span.light #{@milestone.percent_complete}% complete - %span.pull-right= @milestone.expires_at - .progress.progress-info - .progress-bar{style: "width: #{@milestone.percent_complete}%;"} +%hr +.context + %p.lead + Progress: + #{@milestone.closed_items_count} closed + – + #{@milestone.open_items_count} open + + %span.light #{@milestone.percent_complete}% complete + %span.pull-right= @milestone.expires_at + .progress.progress-info + .progress-bar{style: "width: #{@milestone.percent_complete}%;"} %ul.nav.nav-tabs @@ -69,10 +63,10 @@ %span.badge= @users.count .pull-right - = link_to new_project_issue_path(@project, issue: { milestone_id: @milestone.id }), class: "btn btn-small btn-grouped", title: "New Issue" do + = link_to new_project_issue_path(@project, issue: { milestone_id: @milestone.id }), class: "btn btn-grouped", title: "New Issue" do %i.fa.fa-plus New Issue - = link_to 'Browse Issues', project_issues_path(@milestone.project, milestone_id: @milestone.id), class: "btn btn-small edit-milestone-link btn-grouped" + = link_to 'Browse Issues', project_issues_path(@milestone.project, milestone_id: @milestone.id), class: "btn edit-milestone-link btn-grouped" .tab-content .tab-pane.active#tab-issues diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index e77ef84f51c..f320a2b505e 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -5,10 +5,13 @@ = form_for @project, html: { class: 'new_project form-horizontal' } do |f| .form-group.project-name-holder - = f.label :name, class: 'control-label' do - %strong Project name + = f.label :path, class: 'control-label' do + %strong Project path .col-sm-10 - = f.text_field :name, placeholder: "Example Project", class: "form-control", tabindex: 1, autofocus: true + .input-group + = f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 1, autofocus: true + .input-group-addon + \.git - if current_user.can_select_namespace? .form-group @@ -23,22 +26,6 @@ .col-sm-2 .col-sm-10 = link_to "#", class: 'js-toggle-button' do - %i.fa.fa-pencil-square-o - %span Customize repository name? - .js-toggle-content.hide - .form-group - = f.label :path, class: 'control-label' do - %span Repository name - .col-sm-10 - .input-group - = f.text_field :path, class: 'form-control' - %span.input-group-addon .git - - .js-toggle-container - .form-group - .col-sm-2 - .col-sm-10 - = link_to "#", class: 'js-toggle-button' do %i.fa.fa-upload %span Import existing repository? .js-toggle-content.hide diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index 354afd3e2c9..80e7342455b 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -1,7 +1,10 @@ -%li.timeline-entry{ id: dom_id(note), class: dom_class(note), data: { discussion: note.discussion_id } } +%li.timeline-entry{ id: dom_id(note), class: [dom_class(note), ('system-note' if note.system)], data: { discussion: note.discussion_id } } .timeline-entry-inner .timeline-icon - = image_tag avatar_icon(note.author_email), class: "avatar s40" + - if note.system + %span.fa.fa-circle + - else + = image_tag avatar_icon(note.author_email), class: "avatar s40" .timeline-content .note-header .note-actions @@ -17,6 +20,8 @@ = link_to project_note_path(@project, note), title: "Remove comment", method: :delete, data: { confirm: 'Are you sure you want to remove this comment?' }, remote: true, class: "danger js-note-delete" do %i.fa.fa-trash-o.cred Remove + - if note.system + = image_tag avatar_icon(note.author_email), class: "avatar s16" = link_to_member(@project, note.author, avatar: false) %span.author-username = '@' + note.author.username @@ -41,7 +46,7 @@ .note-edit-form = form_for note, url: project_note_path(@project, note), method: :put, remote: true, authenticity_token: true do |f| = render layout: 'projects/md_preview' do - = f.text_area :note, class: 'note_text js-note-text markdown-area js-gfm-input turn-on' + = f.text_area :note, class: 'note_text js-note-text js-gfm-input turn-on' .form-actions.clearfix = f.submit 'Save changes', class: "btn btn-primary btn-save js-comment-button" diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml index 9b06ebe95a4..af6e4567c1b 100644 --- a/app/views/projects/show.html.haml +++ b/app/views/projects/show.html.haml @@ -1,3 +1,6 @@ +- if current_user && can?(current_user, :download_code, @project) + = render 'shared/no_ssh' + = render "home_panel" - readme = @repository.readme diff --git a/app/views/projects/transfer.js.haml b/app/views/projects/transfer.js.haml index 10b0de98c04..6d083c5c516 100644 --- a/app/views/projects/transfer.js.haml +++ b/app/views/projects/transfer.js.haml @@ -1,7 +1,2 @@ -- if @project.errors[:namespace_id].present? - :plain - $("#tab-transfer .errors-holder").replaceWith(errorMessage('#{escape_javascript(@project.errors[:namespace_id].first)}')); - $("#tab-transfer .form-actions input").removeAttr('disabled').removeClass('disabled'); -- else - :plain +:plain location.href = "#{edit_project_path(@project)}"; diff --git a/app/views/shared/_event_filter.html.haml b/app/views/shared/_event_filter.html.haml index ee0b57fbe5a..d07a9e2b924 100644 --- a/app/views/shared/_event_filter.html.haml +++ b/app/views/shared/_event_filter.html.haml @@ -1,5 +1,19 @@ -.event_filter +%ul.nav.nav-pills.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' + + - if current_user + - if current_controller?(:dashboard) + %li.pull-right + = link_to dashboard_path(:atom, { private_token: current_user.private_token }), class: 'rss-btn' do + %i.fa.fa-rss + News Feed + + - if current_controller?(:groups) + %li.pull-right + = link_to group_path(@group, { format: :atom, private_token: current_user.private_token }), title: "Feed", class: 'rss-btn' do + %i.fa.fa-rss + News Feed +%hr diff --git a/app/views/shared/_filter.html.haml b/app/views/shared/_filter.html.haml deleted file mode 100644 index d366dd97a71..00000000000 --- a/app/views/shared/_filter.html.haml +++ /dev/null @@ -1,50 +0,0 @@ -.side-filters - = form_tag filter_path(entity), method: 'get' do - - if current_user - %fieldset.scope-filter - %ul.nav.nav-pills.nav-stacked - %li{class: ("active" if params[:scope] == 'assigned-to-me')} - = link_to filter_path(entity, scope: 'assigned-to-me') do - Assigned to me - %span.pull-right - = assigned_entities_count(current_user, entity, @group) - %li{class: ("active" if params[:scope] == 'authored')} - = link_to filter_path(entity, scope: 'authored') do - Created by me - %span.pull-right - = authored_entities_count(current_user, entity, @group) - %li{class: ("active" if params[:scope] == 'all')} - = link_to filter_path(entity, scope: 'all') do - Everyone's - %span.pull-right - = authorized_entities_count(current_user, entity, @group) - - %fieldset.status-filter - %legend State - %ul.nav.nav-pills - %li{class: ("active" if params[:state] == 'opened')} - = link_to filter_path(entity, state: 'opened') do - Open - %li{class: ("active" if params[:state] == 'closed')} - = link_to filter_path(entity, state: 'closed') do - Closed - %li{class: ("active" if params[:state] == 'all')} - = link_to filter_path(entity, state: 'all') do - All - - %fieldset - %legend Projects - %ul.nav.nav-pills.nav-stacked.nav-small - - @projects.each do |project| - - unless entities_per_project(project, entity).zero? - %li{class: ("active" if params[:project_id] == project.id.to_s)} - = link_to filter_path(entity, project_id: project.id) do - = project.name_with_namespace - %small.pull-right= entities_per_project(project, entity) - - %fieldset - - if params[:state].present? || params[:project_id].present? - = link_to filter_path(entity, state: nil, project_id: nil), class: 'pull-right cgray' do - %i.fa.fa-times - %strong Clear filter - diff --git a/app/views/shared/_group_form.html.haml b/app/views/shared/_group_form.html.haml index 93294e42505..5875f71bac2 100644 --- a/app/views/shared/_group_form.html.haml +++ b/app/views/shared/_group_form.html.haml @@ -1,9 +1,26 @@ +- if @group.persisted? + .form-group + = f.label :name, class: 'control-label' do + Group name + .col-sm-10 + = f.text_field :name, placeholder: 'open-source', class: 'form-control' + .form-group - = f.label :name, class: 'control-label' do - Group name + = f.label :path, class: 'control-label' do + Group path .col-sm-10 - = f.text_field :name, placeholder: 'Example Group', class: 'form-control', - autofocus: local_assigns[:autofocus] || false + .input-group + .input-group-addon + = root_url + = f.text_field :path, placeholder: 'open-source', class: 'form-control', + autofocus: local_assigns[:autofocus] || false + - if @group.persisted? + .bs-callout.bs-callout-danger + %ul + %li Changing group path can have unintended side effects. + %li Renaming group path will rename directory for all related projects + %li It will change web url for access group and group projects. + %li It will change the git path to repositories under this group. .form-group.group-description-holder = f.label :description, 'Details', class: 'control-label' diff --git a/app/views/shared/_issuable_filter.html.haml b/app/views/shared/_issuable_filter.html.haml new file mode 100644 index 00000000000..4f683258fac --- /dev/null +++ b/app/views/shared/_issuable_filter.html.haml @@ -0,0 +1,112 @@ +.issues-filters + .pull-left.append-right-20 + %ul.nav.nav-pills.nav-compact + %li{class: ("active" if params[:state] == 'opened')} + = link_to page_filter_path(state: 'opened') do + %i.fa.fa-exclamation-circle + Open + %li{class: ("active" if params[:state] == 'closed')} + = link_to page_filter_path(state: 'closed') do + %i.fa.fa-check-circle + Closed + %li{class: ("active" if params[:state] == 'all')} + = link_to page_filter_path(state: 'all') do + %i.fa.fa-compass + All + + .dropdown.inline.assignee-filter + %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} + %i.fa.fa-user + %span.light assignee: + - if @assignee.present? + %strong= @assignee.name + - elsif params[:assignee_id] == "0" + Unassigned + - else + Any + %b.caret + %ul.dropdown-menu + %li + = link_to page_filter_path(assignee_id: nil) do + Any + = link_to page_filter_path(assignee_id: 0) do + Unassigned + - @assignees.sort_by(&:name).each do |user| + %li + = link_to page_filter_path(assignee_id: user.id) do + = image_tag avatar_icon(user.email), class: "avatar s16", alt: '' + = user.name + + .dropdown.inline.prepend-left-10.author-filter + %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} + %i.fa.fa-user + %span.light author: + - if @author.present? + %strong= @author.name + - elsif params[:author_id] == "0" + Unassigned + - else + Any + %b.caret + %ul.dropdown-menu + %li + = link_to page_filter_path(author_id: nil) do + Any + = link_to page_filter_path(author_id: 0) do + Unassigned + - @authors.sort_by(&:name).each do |user| + %li + = link_to page_filter_path(author_id: user.id) do + = image_tag avatar_icon(user.email), class: "avatar s16", alt: '' + = user.name + + .dropdown.inline.prepend-left-10.milestone-filter + %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} + %i.fa.fa-clock-o + %span.light milestone: + - if @milestone.present? + %strong= @milestone.title + - elsif params[:milestone_id] == "0" + None (backlog) + - else + Any + %b.caret + %ul.dropdown-menu + %li + = link_to page_filter_path(milestone_id: nil) do + Any + = link_to page_filter_path(milestone_id: 0) do + None (backlog) + - @milestones.each do |milestone| + %li + = link_to page_filter_path(milestone_id: milestone.id) do + %strong= milestone.title + %small.light= milestone.expires_at + + - if @project + .dropdown.inline.prepend-left-10.labels-filter + %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"} + %i.fa.fa-tags + %span.light label: + - if params[:label_name].present? + %strong= params[:label_name] + - else + Any + %b.caret + %ul.dropdown-menu + %li + = link_to page_filter_path(label_name: nil) do + Any + - if @project.labels.any? + - @project.labels.order_by_name.each do |label| + %li + = link_to page_filter_path(label_name: label.name) do + = render_colored_label(label) + - else + %li + = link_to generate_project_labels_path(@project, redirect: request.original_url), method: :post do + %i.fa.fa-plus-circle + Create default labels + + .pull-right + = render 'shared/sort_dropdown' diff --git a/app/views/shared/_no_ssh.html.haml b/app/views/shared/_no_ssh.html.haml index e70eb4d01b9..8e6f802fd3b 100644 --- a/app/views/shared/_no_ssh.html.haml +++ b/app/views/shared/_no_ssh.html.haml @@ -1,14 +1,8 @@ - if cookies[:hide_no_ssh_message].blank? && current_user.require_ssh_key? && !current_user.hide_no_ssh_key - .no-ssh-key-message - .container - You won't be able to pull or push project code via SSH until you #{link_to 'add an SSH key', new_profile_key_path} to your profile - .pull-right.hidden-xs - = link_to "Don't show again", profile_path(user: {hide_no_ssh_key: true}), method: :put, class: 'hide-no-ssh-message', remote: true - | - = link_to 'Remind later', '#', class: 'hide-no-ssh-message' - .links-xs.visible-xs - = link_to "Add key", new_profile_key_path - | - = link_to "Don't show again", profile_path(user: {hide_no_ssh_key: true}), method: :put, class: 'hide-no-ssh-message', remote: true - | - = link_to 'Later', '#', class: 'hide-no-ssh-message' + .no-ssh-key-message.alert.alert-warning.hidden-xs + You won't be able to pull or push project code via SSH until you #{link_to 'add an SSH key', new_profile_key_path} to your profile + + .pull-right + = link_to "Don't show again", profile_path(user: {hide_no_ssh_key: true}), method: :put + | + = link_to 'Remind later', '#', class: 'hide-no-ssh-message' diff --git a/app/views/shared/_project_filter.html.haml b/app/views/shared/_project_filter.html.haml deleted file mode 100644 index ea6a49e1501..00000000000 --- a/app/views/shared/_project_filter.html.haml +++ /dev/null @@ -1,64 +0,0 @@ -.side-filters - = form_tag project_entities_path, method: 'get' do - - if current_user - %fieldset - %ul.nav.nav-pills.nav-stacked - %li{class: ("active" if params[:scope] == 'all')} - = link_to project_filter_path(scope: 'all') do - Everyone's - %span.pull-right - = authorized_entities_count(current_user, entity, @project) - %li{class: ("active" if params[:scope] == 'assigned-to-me')} - = link_to project_filter_path(scope: 'assigned-to-me') do - Assigned to me - %span.pull-right - = assigned_entities_count(current_user, entity, @project) - %li{class: ("active" if params[:scope] == 'created-by-me')} - = link_to project_filter_path(scope: 'created-by-me') do - Created by me - %span.pull-right - = authored_entities_count(current_user, entity, @project) - - %fieldset - %legend State - %ul.nav.nav-pills - %li{class: ("active" if params[:state] == 'opened')} - = link_to project_filter_path(state: 'opened') do - Open - %li{class: ("active" if params[:state] == 'closed')} - = link_to project_filter_path(state: 'closed') do - Closed - %li{class: ("active" if params[:state] == 'all')} - = link_to project_filter_path(state: 'all') do - All - - - if defined?(labels) - %fieldset - %legend - Labels - %small.pull-right - = link_to project_labels_path(@project), class: 'light' do - %i.fa.fa-pencil-square-o - %ul.nav.nav-pills.nav-stacked.nav-small.labels-filter - - @project.labels.order_by_name.each do |label| - %li{class: label_filter_class(label.name)} - = link_to labels_filter_path(label.name) do - = render_colored_label(label) - - if selected_label?(label.name) - .pull-right - %i.fa.fa-times - - - if @project.labels.empty? - .light-well - Create first label at - = link_to 'labels page', project_labels_path(@project) - %br - or #{link_to 'generate', generate_project_labels_path(@project, redirect: redirect), method: :post} default set of labels - - %fieldset - - if %w(state scope milestone_id assignee_id label_name).select { |k| params[k].present? }.any? - = link_to project_entities_path, class: 'cgray pull-right' do - %i.fa.fa-times - %strong Clear filter - - diff --git a/app/views/shared/_sort_dropdown.html.haml b/app/views/shared/_sort_dropdown.html.haml index 54f59245690..93ed9b67336 100644 --- a/app/views/shared/_sort_dropdown.html.haml +++ b/app/views/shared/_sort_dropdown.html.haml @@ -8,15 +8,15 @@ %b.caret %ul.dropdown-menu %li - = link_to project_filter_path(sort: 'newest') do + = link_to page_filter_path(sort: 'newest') do = sort_title_recently_created - = link_to project_filter_path(sort: 'oldest') do + = link_to page_filter_path(sort: 'oldest') do = sort_title_oldest_created - = link_to project_filter_path(sort: 'recently_updated') do + = link_to page_filter_path(sort: 'recently_updated') do = sort_title_recently_updated - = link_to project_filter_path(sort: 'last_updated') do + = link_to page_filter_path(sort: 'last_updated') do = sort_title_oldest_updated - = link_to project_filter_path(sort: 'milestone_due_soon') do + = link_to page_filter_path(sort: 'milestone_due_soon') do Milestone due soon - = link_to project_filter_path(sort: 'milestone_due_later') do + = link_to page_filter_path(sort: 'milestone_due_later') do Milestone due later diff --git a/config/initializers/6_rack_profiler.rb b/config/initializers/6_rack_profiler.rb index a7ee3c59822..b6340287569 100644 --- a/config/initializers/6_rack_profiler.rb +++ b/config/initializers/6_rack_profiler.rb @@ -3,4 +3,6 @@ if Rails.env == 'development' # initialization is skipped so trigger it Rack::MiniProfilerRails.initialize!(Rails.application) + Rack::MiniProfiler.config.position = 'right' + Rack::MiniProfiler.config.start_hidden = true end diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb new file mode 100644 index 00000000000..b2db3a7ea7e --- /dev/null +++ b/config/initializers/doorkeeper.rb @@ -0,0 +1,91 @@ +Doorkeeper.configure do + # Change the ORM that doorkeeper will use. + # Currently supported options are :active_record, :mongoid2, :mongoid3, :mongo_mapper + orm :active_record + + # This block will be called to check whether the resource owner is authenticated or not. + resource_owner_authenticator do + # Put your resource owner authentication logic here. + # Example implementation: + current_user || redirect_to(new_user_session_url) + end + + # If you want to restrict access to the web interface for adding oauth authorized applications, you need to declare the block below. + # admin_authenticator do + # # Put your admin authentication logic here. + # # Example implementation: + # Admin.find_by_id(session[:admin_id]) || redirect_to(new_admin_session_url) + # end + + # Authorization Code expiration time (default 10 minutes). + # authorization_code_expires_in 10.minutes + + # Access token expiration time (default 2 hours). + # If you want to disable expiration, set this to nil. + # access_token_expires_in 2.hours + + # Reuse access token for the same resource owner within an application (disabled by default) + # Rationale: https://github.com/doorkeeper-gem/doorkeeper/issues/383 + # reuse_access_token + + # Issue access tokens with refresh token (disabled by default) + use_refresh_token + + # Provide support for an owner to be assigned to each registered application (disabled by default) + # Optional parameter :confirmation => true (default false) if you want to enforce ownership of + # a registered application + # Note: you must also run the rails g doorkeeper:application_owner generator to provide the necessary support + enable_application_owner :confirmation => true + + # Define access token scopes for your provider + # For more information go to + # https://github.com/doorkeeper-gem/doorkeeper/wiki/Using-Scopes + default_scopes :api + #optional_scopes :write, :update + + # Change the way client credentials are retrieved from the request object. + # By default it retrieves first from the `HTTP_AUTHORIZATION` header, then + # falls back to the `:client_id` and `:client_secret` params from the `params` object. + # Check out the wiki for more information on customization + # client_credentials :from_basic, :from_params + + # Change the way access token is authenticated from the request object. + # By default it retrieves first from the `HTTP_AUTHORIZATION` header, then + # falls back to the `:access_token` or `:bearer_token` params from the `params` object. + # Check out the wiki for more information on customization + access_token_methods :from_access_token_param, :from_bearer_authorization, :from_bearer_param + + # Change the native redirect uri for client apps + # When clients register with the following redirect uri, they won't be redirected to any server and the authorization code will be displayed within the provider + # The value can be any string. Use nil to disable this feature. When disabled, clients must provide a valid URL + # (Similar behaviour: https://developers.google.com/accounts/docs/OAuth2InstalledApp#choosingredirecturi) + # + native_redirect_uri nil#'urn:ietf:wg:oauth:2.0:oob' + + # Specify what grant flows are enabled in array of Strings. The valid + # strings and the flows they enable are: + # + # "authorization_code" => Authorization Code Grant Flow + # "implicit" => Implicit Grant Flow + # "password" => Resource Owner Password Credentials Grant Flow + # "client_credentials" => Client Credentials Grant Flow + # + # If not specified, Doorkeeper enables all the four grant flows. + # + # grant_flows %w(authorization_code implicit password client_credentials) + + # Under some circumstances you might want to have applications auto-approved, + # so that the user skips the authorization step. + # For example if dealing with trusted a application. + # skip_authorization do |resource_owner, client| + # client.superapp? or resource_owner.admin? + # end + + # WWW-Authenticate Realm (default "Doorkeeper"). + # realm "Doorkeeper" + + # Allow dynamic query parameters (disabled by default) + # Some applications require dynamic query parameters on their request_uri + # set to true if you want this to be allowed + # wildcard_redirect_uri false +end diff --git a/config/locales/doorkeeper.en.yml b/config/locales/doorkeeper.en.yml new file mode 100644 index 00000000000..c5b6b75e7f6 --- /dev/null +++ b/config/locales/doorkeeper.en.yml @@ -0,0 +1,73 @@ +en: + activerecord: + errors: + models: + application: + attributes: + redirect_uri: + fragment_present: 'cannot contain a fragment.' + invalid_uri: 'must be a valid URI.' + relative_uri: 'must be an absolute URI.' + mongoid: + errors: + models: + application: + attributes: + redirect_uri: + fragment_present: 'cannot contain a fragment.' + invalid_uri: 'must be a valid URI.' + relative_uri: 'must be an absolute URI.' + mongo_mapper: + errors: + models: + application: + attributes: + redirect_uri: + fragment_present: 'cannot contain a fragment.' + invalid_uri: 'must be a valid URI.' + relative_uri: 'must be an absolute URI.' + doorkeeper: + errors: + messages: + # Common error messages + invalid_request: 'The request is missing a required parameter, includes an unsupported parameter value, or is otherwise malformed.' + invalid_redirect_uri: 'The redirect uri included is not valid.' + unauthorized_client: 'The client is not authorized to perform this request using this method.' + access_denied: 'The resource owner or authorization server denied the request.' + invalid_scope: 'The requested scope is invalid, unknown, or malformed.' + server_error: 'The authorization server encountered an unexpected condition which prevented it from fulfilling the request.' + temporarily_unavailable: 'The authorization server is currently unable to handle the request due to a temporary overloading or maintenance of the server.' + + #configuration error messages + credential_flow_not_configured: 'Resource Owner Password Credentials flow failed due to Doorkeeper.configure.resource_owner_from_credentials being unconfigured.' + resource_owner_authenticator_not_configured: 'Resource Owner find failed due to Doorkeeper.configure.resource_owner_authenticator being unconfiged.' + + # Access grant errors + unsupported_response_type: 'The authorization server does not support this response type.' + + # Access token errors + invalid_client: 'Client authentication failed due to unknown client, no client authentication included, or unsupported authentication method.' + invalid_grant: 'The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.' + unsupported_grant_type: 'The authorization grant type is not supported by the authorization server.' + + # Password Access token errors + invalid_resource_owner: 'The provided resource owner credentials are not valid, or resource owner cannot be found' + + invalid_token: + revoked: "The access token was revoked" + expired: "The access token expired" + unknown: "The access token is invalid" + scopes: + api: Access your API + + flash: + applications: + create: + notice: 'Application created.' + destroy: + notice: 'Application deleted.' + update: + notice: 'Application updated.' + authorized_applications: + destroy: + notice: 'Application revoked.' diff --git a/config/routes.rb b/config/routes.rb index 533e044ca4c..1d571e21b88 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -2,6 +2,11 @@ require 'sidekiq/web' require 'api/api' Gitlab::Application.routes.draw do + use_doorkeeper do + controllers :applications => 'oauth/applications', + :authorized_applications => 'oauth/authorized_applications', + :authorizations => 'oauth/authorizations' + end # # Search # @@ -113,6 +118,7 @@ Gitlab::Application.routes.draw do member do get :history get :design + get :applications put :reset_private_token put :update_username @@ -197,6 +203,7 @@ Gitlab::Application.routes.draw do resources :raw, only: [:show], constraints: {id: /.+/} resources :tree, only: [:show], constraints: {id: /.+/, format: /(html|js)/ } resources :edit_tree, only: [:show, :update], constraints: { id: /.+/ }, path: 'edit' do + # Cannot be GET to differentiate from GET paths that end in preview. post :preview, on: :member end resources :new_tree, only: [:show, :update], constraints: {id: /.+/}, path: 'new' diff --git a/db/fixtures/development/05_users.rb b/db/fixtures/development/05_users.rb index c263dd232af..b697f58d4ef 100644 --- a/db/fixtures/development/05_users.rb +++ b/db/fixtures/development/05_users.rb @@ -16,14 +16,13 @@ Gitlab::Seeder.quiet do (1..5).each do |i| begin - User.seed(:id, [ - id: i + 10, - username: "user#{i}", - name: "User #{i}", - email: "user#{i}@example.com", - confirmed_at: DateTime.now, - password: '12345678' - ]) + User.seed do |s| + s.username = "user#{i}" + s.name = "User #{i}" + s.email = "user#{i}@example.com" + s.confirmed_at = DateTime.now + s.password = '12345678' + end print '.' rescue ActiveRecord::RecordNotSaved print 'F' diff --git a/db/migrate/20141121161704_add_identity_table.rb b/db/migrate/20141121161704_add_identity_table.rb index 6fe63637dfe..a85b0426cec 100644 --- a/db/migrate/20141121161704_add_identity_table.rb +++ b/db/migrate/20141121161704_add_identity_table.rb @@ -14,6 +14,10 @@ SELECT provider, extern_uid, id FROM users WHERE provider IS NOT NULL eos + if index_exists?(:users, ["extern_uid", "provider"]) + remove_index :users, ["extern_uid", "provider"] + end + remove_column :users, :extern_uid remove_column :users, :provider end @@ -34,5 +38,9 @@ eos end drop_table :identities + + unless index_exists?(:users, ["extern_uid", "provider"]) + add_index "users", ["extern_uid", "provider"], name: "index_users_on_extern_uid_and_provider", unique: true, using: :btree + end end end diff --git a/db/migrate/20141216155758_create_doorkeeper_tables.rb b/db/migrate/20141216155758_create_doorkeeper_tables.rb new file mode 100644 index 00000000000..af5aa7d8b73 --- /dev/null +++ b/db/migrate/20141216155758_create_doorkeeper_tables.rb @@ -0,0 +1,42 @@ +class CreateDoorkeeperTables < ActiveRecord::Migration + def change + create_table :oauth_applications do |t| + t.string :name, null: false + t.string :uid, null: false + t.string :secret, null: false + t.text :redirect_uri, null: false + t.string :scopes, null: false, default: '' + t.timestamps + end + + add_index :oauth_applications, :uid, unique: true + + create_table :oauth_access_grants do |t| + t.integer :resource_owner_id, null: false + t.integer :application_id, null: false + t.string :token, null: false + t.integer :expires_in, null: false + t.text :redirect_uri, null: false + t.datetime :created_at, null: false + t.datetime :revoked_at + t.string :scopes + end + + add_index :oauth_access_grants, :token, unique: true + + create_table :oauth_access_tokens do |t| + t.integer :resource_owner_id + t.integer :application_id + t.string :token, null: false + t.string :refresh_token + t.integer :expires_in + t.datetime :revoked_at + t.datetime :created_at, null: false + t.string :scopes + end + + add_index :oauth_access_tokens, :token, unique: true + add_index :oauth_access_tokens, :resource_owner_id + add_index :oauth_access_tokens, :refresh_token, unique: true + end +end diff --git a/db/migrate/20141217125223_add_owner_to_application.rb b/db/migrate/20141217125223_add_owner_to_application.rb new file mode 100644 index 00000000000..7d5e6d07d0f --- /dev/null +++ b/db/migrate/20141217125223_add_owner_to_application.rb @@ -0,0 +1,7 @@ +class AddOwnerToApplication < ActiveRecord::Migration + def change + add_column :oauth_applications, :owner_id, :integer, null: true + add_column :oauth_applications, :owner_type, :string, null: true + add_index :oauth_applications, [:owner_id, :owner_type] + end +end
\ No newline at end of file diff --git a/db/schema.rb b/db/schema.rb index b8335c5841b..73ddb14503f 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20141205134006) do +ActiveRecord::Schema.define(version: 20141217125223) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -249,6 +249,49 @@ ActiveRecord::Schema.define(version: 20141205134006) do add_index "notes", ["project_id"], name: "index_notes_on_project_id", using: :btree add_index "notes", ["updated_at"], name: "index_notes_on_updated_at", using: :btree + create_table "oauth_access_grants", force: true do |t| + t.integer "resource_owner_id", null: false + t.integer "application_id", null: false + t.string "token", null: false + t.integer "expires_in", null: false + t.text "redirect_uri", null: false + t.datetime "created_at", null: false + t.datetime "revoked_at" + t.string "scopes" + end + + add_index "oauth_access_grants", ["token"], name: "index_oauth_access_grants_on_token", unique: true, using: :btree + + create_table "oauth_access_tokens", force: true do |t| + t.integer "resource_owner_id" + t.integer "application_id" + t.string "token", null: false + t.string "refresh_token" + t.integer "expires_in" + t.datetime "revoked_at" + t.datetime "created_at", null: false + t.string "scopes" + end + + add_index "oauth_access_tokens", ["refresh_token"], name: "index_oauth_access_tokens_on_refresh_token", unique: true, using: :btree + add_index "oauth_access_tokens", ["resource_owner_id"], name: "index_oauth_access_tokens_on_resource_owner_id", using: :btree + add_index "oauth_access_tokens", ["token"], name: "index_oauth_access_tokens_on_token", unique: true, using: :btree + + create_table "oauth_applications", force: true do |t| + t.string "name", null: false + t.string "uid", null: false + t.string "secret", null: false + t.text "redirect_uri", null: false + t.string "scopes", default: "", null: false + t.datetime "created_at" + t.datetime "updated_at" + t.integer "owner_id" + t.string "owner_type" + end + + add_index "oauth_applications", ["owner_id", "owner_type"], name: "index_oauth_applications_on_owner_id_and_owner_type", using: :btree + add_index "oauth_applications", ["uid"], name: "index_oauth_applications_on_uid", unique: true, using: :btree + create_table "projects", force: true do |t| t.string "name" t.string "path" diff --git a/doc/README.md b/doc/README.md index 896224fe930..3c8f8ad3d03 100644 --- a/doc/README.md +++ b/doc/README.md @@ -23,6 +23,7 @@ - [Welcome message](customization/welcome_message.md) Add a custom welcome message to the sign-in page. - [Issue closing](customization/issue_closing.md) Customize how to close an issue from commit messages. - [Libravatar](customization/libravatar.md) Use Libravatar for user avatars. +- [Operations](operations/README.md) Keeping GitLab up and running ## Contributor documentation diff --git a/doc/install/installation.md b/doc/install/installation.md index 263259bc2f9..d987e11040b 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -54,7 +54,7 @@ up-to-date and install it. Install the required packages (needed to compile Ruby and native extensions to Ruby gems): - sudo apt-get install -y build-essential zlib1g-dev libyaml-dev libssl-dev libgdbm-dev libreadline-dev libncurses5-dev libffi-dev curl openssh-server redis-server checkinstall libxml2-dev libxslt-dev libcurl4-openssl-dev libicu-dev logrotate python-docutils pkg-config cmake + sudo apt-get install -y build-essential zlib1g-dev libyaml-dev libssl-dev libgdbm-dev libreadline-dev libncurses5-dev libffi-dev curl openssh-server redis-server checkinstall libxml2-dev libxslt-dev libcurl4-openssl-dev libicu-dev logrotate python-docutils pkg-config cmake libkrb5-dev Make sure you have the right version of Git installed @@ -181,9 +181,9 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da ### Clone the Source # Clone GitLab repository - sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 7-5-stable gitlab + sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 7-6-stable gitlab -**Note:** You can change `7-5-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server! +**Note:** You can change `7-6-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server! ### Configure It @@ -278,7 +278,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da GitLab Shell is an SSH access and repository management software developed specially for GitLab. # Run the installation task for gitlab-shell (replace `REDIS_URL` if needed): - sudo -u git -H bundle exec rake gitlab:shell:install[v2.2.0] REDIS_URL=unix:/var/run/redis/redis.sock RAILS_ENV=production + sudo -u git -H bundle exec rake gitlab:shell:install[v2.4.0] REDIS_URL=unix:/var/run/redis/redis.sock RAILS_ENV=production # By default, the gitlab-shell config is generated from your main GitLab config. # You can review (and modify) the gitlab-shell config as follows: @@ -294,9 +294,9 @@ GitLab Shell is an SSH access and repository management software developed speci # When done you see 'Administrator account created:' -**Note:** You can set the Administrator password by supplying it in environmental variable `GITLAB_ROOT_PASSWORD`, eg.: +**Note:** You can set the Administrator/root password by supplying it in environmental variable `GITLAB_ROOT_PASSWORD` as seen below. If you don't set the password (and it is set to the default one) please wait with exposing GitLab to the public internet until the installation is done and you've logged into the server the first time. During the first login you'll be forced to change the default password. - sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production GITLAB_ROOT_PASSWORD=newpassword + sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production GITLAB_ROOT_PASSWORD=yourpassword ### Install Init Script @@ -383,15 +383,17 @@ NOTE: Supply `SANITIZE=true` environment variable to `gitlab:check` to omit proj ### Initial Login -Visit YOUR_SERVER in your web browser for your first GitLab login. The setup has created an admin account for you. You can use it to log in: +Visit YOUR_SERVER in your web browser for your first GitLab login. The setup has created a default admin account for you. You can use it to log in: root 5iveL!fe -**Important Note:** Please go over to your profile page and immediately change the password, so nobody can access your GitLab by using this login information later on. +**Important Note:** On login you'll be prompted to change the password. **Enjoy!** +You can use `sudo service gitlab start` and `sudo service gitlab stop` to start and stop GitLab. + ## Advanced Setup Tips ### Using HTTPS diff --git a/doc/integration/omniauth.md b/doc/integration/omniauth.md index 00adae58dfa..15b4fb622af 100644 --- a/doc/integration/omniauth.md +++ b/doc/integration/omniauth.md @@ -7,6 +7,7 @@ OmniAuth does not prevent standard GitLab authentication or LDAP (if configured) - [Initial OmniAuth Configuration](#initial-omniauth-configuration) - [Supported Providers](#supported-providers) - [Enable OmniAuth for an Existing User](#enable-omniauth-for-an-existing-user) +- [OmniAuth configuration sample when using Omnibus GitLab](https://gitlab.com/gitlab-org/omnibus-gitlab/tree/master#omniauth-google-twitter-github-login) ## Initial OmniAuth Configuration diff --git a/doc/operations/README.md b/doc/operations/README.md new file mode 100644 index 00000000000..31b1b583b0c --- /dev/null +++ b/doc/operations/README.md @@ -0,0 +1,3 @@ +# GitLab operations + +- [Sidekiq MemoryKiller](sidekiq_memory_killer.md) diff --git a/doc/operations/sidekiq_memory_killer.md b/doc/operations/sidekiq_memory_killer.md new file mode 100644 index 00000000000..867b01b0d5a --- /dev/null +++ b/doc/operations/sidekiq_memory_killer.md @@ -0,0 +1,38 @@ +# Sidekiq MemoryKiller + +The GitLab Rails application code suffers from memory leaks. For web requests +this problem is made manageable using +[unicorn-worker-killer](https://github.com/kzk/unicorn-worker-killer) which +restarts Unicorn worker processes in between requests when needed. The Sidekiq +MemoryKiller applies the same approach to the Sidekiq processes used by GitLab +to process background jobs. + +Unlike unicorn-worker-killer, which is enabled by default for all GitLab +installations since GitLab 6.4, the Sidekiq MemoryKiller is enabled by default +_only_ for Omnibus packages. The reason for this is that the MemoryKiller +relies on Runit to restart Sidekiq after a memory-induced shutdown and GitLab +installations from source do not all use Runit or an equivalent. + +With the default settings, the MemoryKiller will cause a Sidekiq restart no +more often than once every 15 minutes, with the restart causing about one +minute of delay for incoming background jobs. + +## Configuring the MemoryKiller + +The MemoryKiller is controlled using environment variables. + +- `SIDEKIQ_MEMORY_KILLER_MAX_RSS`: if this variable is set, and its value is + greater than 0, then after each Sidekiq job, the MemoryKiller will check the + RSS of the Sidekiq process that executed the job. If the RSS of the Sidekiq + process (expressed in kilobytes) exceeds SIDEKIQ_MEMORY_KILLER_MAX_RSS, a + delayed shutdown is triggered. The default value for Omnibus packages is set + [in the omnibus-gitlab + repository](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-cookbooks/gitlab/attributes/default.rb). +- `SIDEKIQ_MEMORY_KILLER_GRACE_TIME`: defaults 900 seconds (15 minutes). When + a shutdown is triggered, the Sidekiq process will keep working normally for + another 15 minutes. +- `SIDEKIQ_MEMORY_KILLER_SHUTDOWN_WAIT`: defaults to 30 seconds. When the grace + time has expired, the MemoryKiller tells Sidekiq to stop accepting new jobs. + Existing jobs get 30 seconds to finish. After that, the MemoryKiller tells + Sidekiq to shut down, and an external supervision mechanism (e.g. Runit) must + restart Sidekiq. diff --git a/doc/raketasks/README.md b/doc/raketasks/README.md index 9e2f697bca6..770b7a70fe0 100644 --- a/doc/raketasks/README.md +++ b/doc/raketasks/README.md @@ -1,5 +1,8 @@ +# Rake tasks + - [Backup restore](backup_restore.md) - [Cleanup](cleanup.md) +- [Features](features.md) - [Maintenance](maintenance.md) and self-checks - [User management](user_management.md) - [Web hooks](web_hooks.md) diff --git a/doc/release/monthly.md b/doc/release/monthly.md index 0700f24ab76..b31fd885404 100644 --- a/doc/release/monthly.md +++ b/doc/release/monthly.md @@ -100,9 +100,9 @@ List any major changes here, so the user is aware of them before starting to upg - Web server changes - File structure changes -#### 1. Make backup +#### 1. Stop server -#### 2. Stop server +#### 2. Make backup #### 3. Do users need to update dependencies like `git`? @@ -200,7 +200,7 @@ Add to your local `gitlab-ci/.git/config`: # **4 workdays before release - Release RC1** -### **1. Determine QA person +### **1. Determine QA person** Notify person of QA day. @@ -215,6 +215,7 @@ It is important to do this as soon as possible, so we can catch any errors befor ### **3. Prepare the blog post** - Start with a complete copy of the [release blog template](https://gitlab.com/gitlab-com/www-gitlab-com/blob/master/doc/release_blog_template.md) and fill it out. +- Make sure the blog post contains information about the GitLab CI release. - Check the changelog of CE and EE for important changes. - Also check the CI changelog - Add a proposed tweet text to the blog post WIP MR description. @@ -224,8 +225,8 @@ It is important to do this as soon as possible, so we can catch any errors befor - Create WIP MR for adding MVP to MVP page on website - Add a note if there are security fixes: This release fixes an important security issue and we advise everyone to upgrade as soon as possible. - Create a merge request on [GitLab.com](https://gitlab.com/gitlab-com/www-gitlab-com/tree/master) -- Assign to one reviewer who will fix spelling issues by editing the branch (can use the online editor) -- After the reviewer is finished the whole team will be mentioned to give their suggestions via line comments +- Assign to one reviewer who will fix spelling issues by editing the branch (either with a git client or by using the online editor) +- Comment to the reviewer: '@person Please mention the whole team as soon as you are done (3 workdays before release at the latest)' ### **4. Create a regressions issue** @@ -238,7 +239,7 @@ The release manager will comment here about the plans for patch releases. Assign the issue to the release manager and /cc all the core-team members active on the issue tracker. If there are any known bugs in the release add them immediately. -### **4. Tweet** +### **5. Tweet** Tweet about the RC release: @@ -246,6 +247,10 @@ Tweet about the RC release: # **1 workdays before release - Preparation** +### **0. Doublecheck blog post** + +Doublecheck the everyone has been mentioned in the blog post. + ### **1. Pre QA merge** Merge CE into EE before doing the QA. @@ -265,7 +270,7 @@ Create an issue with description of a problem, if it is quick fix fix it yoursel **NOTE** If there is a problem that cannot be fixed in a timely manner, reverting the feature is an option! If the feature is reverted, create an issue about it in order to discuss the next steps after the release. -# **22nd - Release CE, EE and CI** +# **Workday before release - Create Omnibus tags and build packages** **Make sure EE `x-x-stable-ee` has latest changes from CE `x-x-stable`** @@ -302,20 +307,24 @@ Follow the [release doc in the Omnibus repository](https://gitlab.com/gitlab-org This can happen before tagging because Omnibus uses tags in its own repo and SHA1's to refer to the GitLab codebase. -### **4. Publish packages for new release** +# **22nd - Release CE, EE and CI** + +### **1. Publish packages for new release** Update `downloads/index.html` and `downloads/archive/index.html` in `www-gitlab-com` repository. -### **5. Publish blog for new release** +### **2. Publish blog for new release** Merge the [blog merge request](#1-prepare-the-blog-post) in `www-gitlab-com` repository. -### **6. Tweet to blog** +### **3. Tweet to blog** Send out a tweet to share the good news with the world. List the most important features and link to the blog post. -Proposed tweet for CE "GitLab X.X is released! It brings *** <link-to-blogpost>" +Proposed tweet "Release of GitLab X.X & CI Y.Y! FEATURE, FEATURE and FEATURE <link-to-blogpost> #gitlab" + +Consider creating a post on Hacker News. # **1 workday after release - Update GitLab.com** diff --git a/doc/update/6.x-or-7.x-to-7.5.md b/doc/update/6.x-or-7.x-to-7.6.md index c9b95c62611..883a654dcd8 100644 --- a/doc/update/6.x-or-7.x-to-7.5.md +++ b/doc/update/6.x-or-7.x-to-7.6.md @@ -1,6 +1,6 @@ -# From 6.x or 7.x to 7.5 +# From 6.x or 7.x to 7.6 -This allows you to upgrade any version of GitLab from 6.0 and up (including 7.0 and up) to 7.5. +This allows you to upgrade any version of GitLab from 6.0 and up (including 7.0 and up) to 7.6. ## Global issue numbers @@ -70,7 +70,7 @@ sudo -u git -H git checkout -- db/schema.rb # local changes will be restored aut For GitLab Community Edition: ```bash -sudo -u git -H git checkout 7-5-stable +sudo -u git -H git checkout 7-6-stable ``` OR @@ -78,7 +78,7 @@ OR For GitLab Enterprise Edition: ```bash -sudo -u git -H git checkout 7-5-stable-ee +sudo -u git -H git checkout 7-6-stable-ee ``` ## 4. Install additional packages @@ -119,7 +119,7 @@ sudo apt-get install pkg-config cmake ```bash cd /home/git/gitlab-shell sudo -u git -H git fetch -sudo -u git -H git checkout v2.2.0 +sudo -u git -H git checkout v2.4.0 ``` ## 7. Install libs, migrations, etc. @@ -154,14 +154,14 @@ sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab TIP: to see what changed in `gitlab.yml.example` in this release use next command: ``` -git diff 6-0-stable:config/gitlab.yml.example 7-5-stable:config/gitlab.yml.example +git diff 6-0-stable:config/gitlab.yml.example 7-6-stable:config/gitlab.yml.example ``` -* Make `/home/git/gitlab/config/gitlab.yml` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-5-stable/config/gitlab.yml.example but with your settings. -* Make `/home/git/gitlab/config/unicorn.rb` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-5-stable/config/unicorn.rb.example but with your settings. -* Make `/home/git/gitlab-shell/config.yml` the same as https://gitlab.com/gitlab-org/gitlab-shell/blob/v2.2.0/config.yml.example but with your settings. -* HTTP setups: Make `/etc/nginx/sites-available/gitlab` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-5-stable/lib/support/nginx/gitlab but with your settings. -* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-5-stable/lib/support/nginx/gitlab-ssl but with your settings. +* Make `/home/git/gitlab/config/gitlab.yml` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-6-stable/config/gitlab.yml.example but with your settings. +* Make `/home/git/gitlab/config/unicorn.rb` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-6-stable/config/unicorn.rb.example but with your settings. +* Make `/home/git/gitlab-shell/config.yml` the same as https://gitlab.com/gitlab-org/gitlab-shell/blob/v2.4.0/config.yml.example but with your settings. +* HTTP setups: Make `/etc/nginx/sites-available/gitlab` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-6-stable/lib/support/nginx/gitlab but with your settings. +* HTTPS setups: Make `/etc/nginx/sites-available/gitlab-ssl` the same as https://gitlab.com/gitlab-org/gitlab-ce/blob/7-6-stable/lib/support/nginx/gitlab-ssl but with your settings. * Copy rack attack middleware config ```bash diff --git a/doc/update/7.5-to-7.6.md b/doc/update/7.5-to-7.6.md index deee73fe560..35cd437fdc4 100644 --- a/doc/update/7.5-to-7.6.md +++ b/doc/update/7.5-to-7.6.md @@ -1,7 +1,5 @@ # From 7.5 to 7.6 -**7.6 is not yet released. This is a preliminary upgrade guide.** - ### 0. Stop server sudo service gitlab stop @@ -39,12 +37,14 @@ sudo -u git -H git checkout 7-6-stable-ee ```bash cd /home/git/gitlab-shell sudo -u git -H git fetch -sudo -u git -H git checkout v2.2.0 +sudo -u git -H git checkout v2.4.0 ``` ### 4. Install libs, migrations, etc. ```bash +sudo apt-get install libkrb5-dev + cd /home/git/gitlab # MySQL installations (note: the line below states '--without ... postgres') diff --git a/doc/update/mysql_to_postgresql.md b/doc/update/mysql_to_postgresql.md index 695c083d361..229689392b8 100644 --- a/doc/update/mysql_to_postgresql.md +++ b/doc/update/mysql_to_postgresql.md @@ -13,7 +13,7 @@ sudo service gitlab stop git clone https://github.com/gitlabhq/mysql-postgresql-converter.git cd mysql-postgresql-converter -mysqldump --compatible=postgresql --default-character-set=utf8 -r databasename.mysql -u root gitlabhq_production +mysqldump --compatible=postgresql --default-character-set=utf8 -r databasename.mysql -u root gitlabhq_production -p python db_converter.py databasename.mysql databasename.psql # Import the database dump as the application database user @@ -94,7 +94,7 @@ sudo -u git -H mv tmp/backups/TIMESTAMP_gitlab_backup.tar tmp/backups/postgresql # Create a separate database dump with PostgreSQL compatibility cd tmp/backups/postgresql -sudo -u git -H mysqldump --compatible=postgresql --default-character-set=utf8 -r gitlabhq_production.mysql -u root gitlabhq_production +sudo -u git -H mysqldump --compatible=postgresql --default-character-set=utf8 -r gitlabhq_production.mysql -u root gitlabhq_production -p # Clone the database converter sudo -u git -H git clone https://github.com/gitlabhq/mysql-postgresql-converter.git diff --git a/doc/update/upgrader.md b/doc/update/upgrader.md index 0a9f242d9ab..5016ee4baad 100644 --- a/doc/update/upgrader.md +++ b/doc/update/upgrader.md @@ -23,7 +23,7 @@ If you have local changes to your GitLab repository the script will stash them a ## 2. Run GitLab upgrade tool -Note: GitLab 7.2 adds `pkg-config` and `cmake` as dependency. Please check the dependencies in the [installation guide.](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md#1-packages-dependencies) +Note: GitLab 7.6 adds `libkrb5-dev` as a dependency (installed by default on Ubuntu and OSX) while 7.2 adds `pkg-config` and `cmake` as dependency. Please check the dependencies in the [installation guide.](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md#1-packages-dependencies) # Starting with GitLab version 7.0 upgrader script has been moved to bin directory cd /home/git/gitlab diff --git a/doc/workflow/README.md b/doc/workflow/README.md index c26d85e9955..f0e0f51b1ac 100644 --- a/doc/workflow/README.md +++ b/doc/workflow/README.md @@ -1,3 +1,5 @@ +# Workflow + - [Workflow](workflow.md) - [Project Features](project_features.md) - [Authorization for merge requests](authorization_for_merge_requests.md) diff --git a/doc/workflow/gitlab_flow.md b/doc/workflow/gitlab_flow.md index f8fd7c97e2a..1dbff60cbfd 100644 --- a/doc/workflow/gitlab_flow.md +++ b/doc/workflow/gitlab_flow.md @@ -1,6 +1,6 @@  -# Introduction +## Introduction Version management with git makes branching and merging much easier than older versioning systems such as SVN. This allows a wide variety of branching strategies and workflows. @@ -29,9 +29,9 @@ People have a hard time figuring out which branch they should develop on or depl Frequently the reaction to this problem is to adopt a standardized pattern such as [git flow](http://nvie.com/posts/a-successful-git-branching-model/) and [GitHub flow](http://scottchacon.com/2011/08/31/github-flow.html) We think there is still room for improvement and will detail a set of practices we call GitLab flow. -# Git flow and its problems +## Git flow and its problems -[ +[ Git flow was one of the first proposals to use git branches and it has gotten a lot of attention. It advocates a master branch and a separate develop branch as well as supporting branches for features, releases and hotfixes. @@ -50,7 +50,7 @@ Frequently developers make a mistake and for example changes are only merged int The root cause of these errors is that git flow is too complex for most of the use cases. And doing releases doesn't automatically mean also doing hotfixes. -# GitHub flow as a simpler alternative +## GitHub flow as a simpler alternative  @@ -62,13 +62,13 @@ Merging everything into the master branch and deploying often means you minimize But this flow still leaves a lot of questions unanswered regarding deployments, environments, releases and integrations with issues. With GitLab flow we offer additional guidance for these questions. -# Production branch with GitLab flow +## Production branch with GitLab flow  GitHub flow does assume you are able to deploy to production every time you merge a feature branch. This is possible for SaaS applications but are many cases where this is not possible. -One would be a situation where you are not in control of the exact release moment, for example an iOS application that needs to pass AppStore validation. +One would be a situation where you are not in control of the exact release moment, for example an iOS application that needs to pass App Store validation. Another example is when you have deployment windows (workdays from 10am to 4pm when the operations team is at full capacity) but you also merge code at other times. In these cases you can make a production branch that reflects the deployed code. You can deploy a new version by merging in master to the production branch. @@ -78,7 +78,7 @@ This time is pretty accurate if you automatically deploy your production branch. If you need a more exact time you can have your deployment script create a tag on each deployment. This flow prevents the overhead of releasing, tagging and merging that is common to git flow. -# Environment branches with GitLab flow +## Environment branches with GitLab flow  @@ -93,7 +93,7 @@ If master is good to go (it should be if you a practicing [continuous delivery]( If this is not possible because more manual testing is required you can send merge requests from the feature branch to the downstream branches. An 'extreme' version of environment branches are setting up an environment for each feature branch as done by [Teatro](http://teatro.io/). -# Release branches with GitLab flow +## Release branches with GitLab flow  @@ -109,7 +109,7 @@ Every time a bug-fix is included in a release branch the patch version is raised Some projects also have a stable branch that points to the same commit as the latest released branch. In this flow it is not common to have a production branch (or git flow master branch). -# Merge/pull requests with GitLab flow +## Merge/pull requests with GitLab flow  @@ -134,7 +134,7 @@ If the assigned person does not feel comfortable they can close the merge reques In GitLab it is common to protect the long-lived branches (e.g. the master branch) so that normal developers [can't modify these protected branches](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/permissions/permissions.md). So if you want to merge it into a protected branch you assign it to someone with master authorizations. -# Issues with GitLab flow +## Issues with GitLab flow  @@ -168,7 +168,7 @@ In this case it is no problem to reuse the same branch name since it was deleted At any time there is at most one branch for every issue. It is possible that one feature branch solves more than one issue. -# Linking and closing issues from merge requests +## Linking and closing issues from merge requests  @@ -181,7 +181,7 @@ If you only want to make the reference without closing the issue you can also ju If you have an issue that spans across multiple repositories, the best thing is to create an issue for each repository and link all issues to a parent issue. -# Squashing commits with rebase +## Squashing commits with rebase  @@ -189,7 +189,7 @@ With git you can use an interactive rebase (rebase -i) to squash multiple commit This functionality is useful if you made a couple of commits for small changes during development and want to replace them with a single commit or if you want to make the order more logical. However you should never rebase commits you have pushed to a remote server. Somebody can have referred to the commits or cherry-picked them. -When you rebase you change the identifier (SHA1) of the commit and this is confusing. +When you rebase you change the identifier (SHA-1) of the commit and this is confusing. If you do that the same change will be known under multiple identifiers and this can cause much confusion. If people already reviewed your code it will be hard for them to review only the improvements you made since then if you have rebased everything into one commit. @@ -207,7 +207,7 @@ If you revert a merge and you change your mind, revert the revert instead of mer Being able to revert a merge is a good reason always to create a merge commit when you merge manually with the `--no-ff` option. Git management software will always create a merge commit when you accept a merge request. -# Do not order commits with rebase +## Do not order commits with rebase  @@ -231,8 +231,8 @@ The last reason for creating merge commits is having long lived branches that yo Martin Fowler, in [his article about feature branches](http://martinfowler.com/bliki/FeatureBranch.html) talks about this Continuous Integration (CI). At GitLab we are guilty of confusing CI with branch testing. Quoting Martin Fowler: "I've heard people say they are doing CI because they are running builds, perhaps using a CI server, on every branch with every commit. That's continuous building, and a Good Thing, but there's no integration, so it's not CI.". -The solution to prevent many merge commits is to keep your feature branches shortlived, the vast majority should take less than one day of work. -If your feature branches commenly take more than a day of work, look into ways to create smaller units of work and/or use [feature toggles](http://martinfowler.com/bliki/FeatureToggle.html). +The solution to prevent many merge commits is to keep your feature branches short-lived, the vast majority should take less than one day of work. +If your feature branches commonly take more than a day of work, look into ways to create smaller units of work and/or use [feature toggles](http://martinfowler.com/bliki/FeatureToggle.html). As for the long running branches that take more than one day there are two strategies. In a CI strategy you can merge in master at the start of the day to prevent painful merges at a later time. In a synchronization point strategy you only merge in from well defined points in time, for example a tagged release. @@ -244,7 +244,7 @@ Developing software happen in small messy steps and it is OK to have your histor You can use tools to view the network graphs of commits and understand the messy history that created your code. If you rebase code the history is incorrect, and there is no way for tools to remedy this because they can't deal with changing commit identifiers. -# Voting on merge requests +## Voting on merge requests  @@ -252,7 +252,7 @@ It is common to voice approval or disapproval by using +1 or -1 emoticons. In GitLab the +1 and -1 are aggregated and shown at the top of the merge request. As a rule of thumb anything that doesn't have two times more +1's than -1's is suspect and should not be merged yet. -# Pushing and removing branches +## Pushing and removing branches  @@ -266,7 +266,7 @@ This ensures that the branch overview in the repository management software show This also ensures that when someone reopens the issue a new branch with the same name can be used without problem. When you reopen an issue you need to create a new merge request. -# Committing often and with the right message +## Committing often and with the right message  @@ -282,7 +282,7 @@ Some words that are bad commit messages because they don't contain munch informa The word fix or fixes is also a red flag, unless it comes after the commit sentence and references an issue number. To see more information about the formatting of commit messages please see this great [blog post by Tim Pope](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html). -# Testing before merging +## Testing before merging  @@ -299,7 +299,7 @@ If there are no merge conflicts and the feature branches are short lived the ris If there are merge conflicts you merge the master branch into the feature branch and the CI server will rerun the tests. If you have long lived feature branches that last for more than a few days you should make your issues smaller. -# Merging in other code +## Merging in other code  diff --git a/docker/Dockerfile b/docker/Dockerfile index aea59916c7a..5d0880b8c88 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -2,16 +2,16 @@ FROM ubuntu:14.04 # Install required packages RUN apt-get update -q \ - && DEBIAN_FRONTEND=noninteractive apt-get install -qy \ + && DEBIAN_FRONTEND=noninteractive apt-get install -qy --no-install-recommends \ + ca-certificates \ openssh-server \ - wget \ - && apt-get clean + wget # Download & Install GitLab # If the Omnibus package version below is outdated please contribute a merge request to update it. # If you run GitLab Enterprise Edition point it to a location where you have downloaded it. RUN TMP_FILE=$(mktemp); \ - wget -q -O $TMP_FILE https://downloads-packages.s3.amazonaws.com/ubuntu-14.04/gitlab_7.5.2-omnibus.5.2.1.ci-1_amd64.deb \ + wget -q -O $TMP_FILE https://downloads-packages.s3.amazonaws.com/ubuntu-14.04/gitlab_7.5.3-omnibus.5.2.1.ci-1_amd64.deb \ && dpkg -i $TMP_FILE \ && rm -f $TMP_FILE diff --git a/features/explore/groups.feature b/features/explore/groups.feature index b50a3e766c6..c11634bd74a 100644 --- a/features/explore/groups.feature +++ b/features/explore/groups.feature @@ -28,7 +28,6 @@ Feature: Explore Groups Given group "TestGroup" has internal project "Internal" When I sign in as a user And I visit group "TestGroup" issues page - And I change filter to Everyone's Then I should see project "Internal" items And I should not see project "Enterprise" items @@ -36,7 +35,6 @@ Feature: Explore Groups Given group "TestGroup" has internal project "Internal" When I sign in as a user And I visit group "TestGroup" merge requests page - And I change filter to Everyone's Then I should see project "Internal" items And I should not see project "Enterprise" items @@ -94,7 +92,6 @@ Feature: Explore Groups Given group "TestGroup" has public project "Community" When I sign in as a user And I visit group "TestGroup" issues page - And I change filter to Everyone's Then I should see project "Community" items And I should see project "Internal" items And I should not see project "Enterprise" items @@ -104,7 +101,6 @@ Feature: Explore Groups Given group "TestGroup" has public project "Community" When I sign in as a user And I visit group "TestGroup" merge requests page - And I change filter to Everyone's Then I should see project "Community" items And I should see project "Internal" items And I should not see project "Enterprise" items diff --git a/features/profile/profile.feature b/features/profile/profile.feature index d7fa370fe2a..fd132e1cd80 100644 --- a/features/profile/profile.feature +++ b/features/profile/profile.feature @@ -71,6 +71,20 @@ Feature: Profile And I click on my profile picture Then I should see my user page + Scenario: I can manage application + Given I visit profile applications page + Then I click on new application button + And I should see application form + Then I fill application form out and submit + And I see application + Then I click edit + And I see edit application form + Then I change name of application and submit + And I see that application was changed + Then I visit profile applications page + And I click to remove application + Then I see that application is removed + @javascript Scenario: I change my application theme Given I visit profile design page @@ -101,4 +115,4 @@ Feature: Profile Scenario: I see the password strength indicator with success Given I visit profile password page When I try to set a strong password - Then I should see the input field green
\ No newline at end of file + Then I should see the input field green diff --git a/features/steps/admin/groups.rb b/features/steps/admin/groups.rb index d69a87cd07e..4171398e568 100644 --- a/features/steps/admin/groups.rb +++ b/features/steps/admin/groups.rb @@ -22,7 +22,7 @@ class Spinach::Features::AdminGroups < Spinach::FeatureSteps end step 'submit form with new group info' do - fill_in 'group_name', with: 'gitlab' + fill_in 'group_path', with: 'gitlab' fill_in 'group_description', with: 'Group description' click_button "Create group" end diff --git a/features/steps/dashboard/issues.rb b/features/steps/dashboard/issues.rb index 2a5850d091b..b77113e3974 100644 --- a/features/steps/dashboard/issues.rb +++ b/features/steps/dashboard/issues.rb @@ -35,14 +35,20 @@ class Spinach::Features::DashboardIssues < Spinach::FeatureSteps end step 'I click "Authored by me" link' do - within ".scope-filter" do - click_link 'Created by me' + within ".assignee-filter" do + click_link "Any" + end + within ".author-filter" do + click_link current_user.name end end step 'I click "All" link' do - within ".scope-filter" do - click_link "Everyone's" + within ".author-filter" do + click_link "Any" + end + within ".assignee-filter" do + click_link "Any" end end diff --git a/features/steps/dashboard/merge_requests.rb b/features/steps/dashboard/merge_requests.rb index 75e53173d3f..6261c89924c 100644 --- a/features/steps/dashboard/merge_requests.rb +++ b/features/steps/dashboard/merge_requests.rb @@ -39,14 +39,20 @@ class Spinach::Features::DashboardMergeRequests < Spinach::FeatureSteps end step 'I click "Authored by me" link' do - within ".scope-filter" do - click_link 'Created by me' + within ".assignee-filter" do + click_link "Any" + end + within ".author-filter" do + click_link current_user.name end end step 'I click "All" link' do - within ".scope-filter" do - click_link "Everyone's" + within ".author-filter" do + click_link "Any" + end + within ".assignee-filter" do + click_link "Any" end end diff --git a/features/steps/groups.rb b/features/steps/groups.rb index 616a297db99..f09d751dba3 100644 --- a/features/steps/groups.rb +++ b/features/steps/groups.rb @@ -77,7 +77,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps end step 'submit form with new group "Samurai" info' do - fill_in 'group_name', with: 'Samurai' + fill_in 'group_path', with: 'Samurai' fill_in 'group_description', with: 'Tokugawa Shogunate' click_button "Create group" end @@ -89,17 +89,17 @@ class Spinach::Features::Groups < Spinach::FeatureSteps step 'I should see newly created group "Samurai"' do page.should have_content "Samurai" page.should have_content "Tokugawa Shogunate" - page.should have_content "Currently you are only seeing events from the" end step 'I change group "Owned" name to "new-name"' do fill_in 'group_name', with: 'new-name' + fill_in 'group_path', with: 'new-name' click_button "Save group" end step 'I should see new group "Owned" name' do within ".navbar-gitlab" do - page.should have_content "group: new-name" + page.should have_content "new-name" end end @@ -188,7 +188,6 @@ class Spinach::Features::Groups < Spinach::FeatureSteps end step 'I should see group milestone with descriptions and expiry date' do - page.should have_content('Lorem Ipsum is simply dummy text of the printing and typesetting industry') page.should have_content('expires at Aug 20, 2114') end diff --git a/features/steps/profile/profile.rb b/features/steps/profile/profile.rb index 38aaadcd28d..29fc7e68dac 100644 --- a/features/steps/profile/profile.rb +++ b/features/steps/profile/profile.rb @@ -221,4 +221,54 @@ class Spinach::Features::Profile < Spinach::FeatureSteps step 'I should see groups I belong to' do page.should have_css('.profile-groups-avatars', visible: true) end + + step 'I click on new application button' do + click_on 'New Application' + end + + step 'I should see application form' do + page.should have_content "New application" + end + + step 'I fill application form out and submit' do + fill_in :doorkeeper_application_name, with: 'test' + fill_in :doorkeeper_application_redirect_uri, with: 'https://test.com' + click_on "Submit" + end + + step 'I see application' do + page.should have_content "Application: test" + page.should have_content "Application Id" + page.should have_content "Secret" + end + + step 'I click edit' do + click_on "Edit" + end + + step 'I see edit application form' do + page.should have_content "Edit application" + end + + step 'I change name of application and submit' do + page.should have_content "Edit application" + fill_in :doorkeeper_application_name, with: 'test_changed' + click_on "Submit" + end + + step 'I see that application was changed' do + page.should have_content "test_changed" + page.should have_content "Application Id" + page.should have_content "Secret" + end + + step 'I click to remove application' do + within '.oauth-applications' do + click_on "Destroy" + end + end + + step "I see that application is removed" do + page.find(".oauth-applications").should_not have_content "test_changed" + end end diff --git a/features/steps/project/commits/commits.rb b/features/steps/project/commits/commits.rb index 935f313e298..d515ee1ac11 100644 --- a/features/steps/project/commits/commits.rb +++ b/features/steps/project/commits/commits.rb @@ -78,14 +78,14 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps end step 'I click side-by-side diff button' do - click_link "Side-by-side Diff" + click_link "Side-by-side" end step 'I see side-by-side diff button' do - page.should have_content "Side-by-side Diff" + page.should have_content "Side-by-side" end step 'I see inline diff button' do - page.should have_content "Inline Diff" + page.should have_content "Inline" end end diff --git a/features/steps/project/create.rb b/features/steps/project/create.rb index e1062a6ce39..6b07b62f16f 100644 --- a/features/steps/project/create.rb +++ b/features/steps/project/create.rb @@ -3,7 +3,7 @@ class Spinach::Features::ProjectCreate < Spinach::FeatureSteps include SharedPaths step 'fill project form with valid data' do - fill_in 'project_name', with: 'Empty' + fill_in 'project_path', with: 'Empty' click_button "Create project" end diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb index d5e060bdbe8..28928d602d6 100644 --- a/features/steps/project/merge_requests.rb +++ b/features/steps/project/merge_requests.rb @@ -57,9 +57,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps end step 'I click link "Close"' do - within '.page-title' do - click_link "Close" - end + first(:css, '.close-mr-link').click end step 'I submit new merge request "Wiki Feature"' do @@ -111,6 +109,10 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps end step 'I click on the commit in the merge request' do + within '.merge-request-tabs' do + click_link 'Commits' + end + within '.mr-commits' do click_link Commit.truncate_sha(sample_commit.id) end @@ -181,13 +183,11 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps end step 'I click link "Reopen"' do - within '.page-title' do - click_link "Reopen" - end + first(:css, '.reopen-mr-link').click end step 'I should see reopened merge request "Bug NS-04"' do - within '.state-label' do + within '.issue-box' do page.should have_content "Open" end end @@ -265,7 +265,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps end step 'I click Side-by-side Diff tab' do - click_link 'Side-by-side Diff' + click_link 'Side-by-side' end step 'I should see comments on the side-by-side diff page' do diff --git a/features/steps/shared/active_tab.rb b/features/steps/shared/active_tab.rb index f41b59a6f2b..c229864bc83 100644 --- a/features/steps/shared/active_tab.rb +++ b/features/steps/shared/active_tab.rb @@ -2,7 +2,7 @@ module SharedActiveTab include Spinach::DSL def ensure_active_main_tab(content) - find('.main-nav li.active').should have_content(content) + find('.nav-sidebar > li.active').should have_content(content) end def ensure_active_sub_tab(content) @@ -10,11 +10,11 @@ module SharedActiveTab end def ensure_active_sub_nav(content) - find('div.content ul.nav-stacked-menu li.active').should have_content(content) + find('.sidebar-subnav > li.active').should have_content(content) end step 'no other main tabs should be active' do - page.should have_selector('.main-nav li.active', count: 1) + page.should have_selector('.nav-sidebar > li.active', count: 1) end step 'no other sub tabs should be active' do @@ -22,7 +22,7 @@ module SharedActiveTab end step 'no other sub navs should be active' do - page.should have_selector('div.content ul.nav-stacked-menu li.active', count: 1) + page.should have_selector('.sidebar-subnav > li.active', count: 1) end step 'the active main tab should be Home' do diff --git a/features/steps/shared/issuable.rb b/features/steps/shared/issuable.rb index a0150e90380..41db2612f26 100644 --- a/features/steps/shared/issuable.rb +++ b/features/steps/shared/issuable.rb @@ -2,7 +2,7 @@ module SharedIssuable include Spinach::DSL def edit_issuable - find('.issue-btn-group').click_link 'Edit' + find(:css, '.issuable-edit').click end step 'I click link "Edit" for the merge request' do diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb index 5f292255ce1..b60d290ae9c 100644 --- a/features/steps/shared/paths.rb +++ b/features/steps/shared/paths.rb @@ -1,6 +1,7 @@ module SharedPaths include Spinach::DSL include RepoHelpers + include DashboardHelper step 'I visit new project page' do visit new_project_path @@ -71,11 +72,11 @@ module SharedPaths end step 'I visit dashboard issues page' do - visit issues_dashboard_path + visit assigned_issues_dashboard_path end step 'I visit dashboard merge requests page' do - visit merge_requests_dashboard_path + visit assigned_mrs_dashboard_path end step 'I visit dashboard search page' do @@ -94,6 +95,10 @@ module SharedPaths visit profile_path end + step 'I visit profile applications page' do + visit applications_profile_path + end + step 'I visit profile password page' do visit edit_profile_password_path end diff --git a/lib/api/api.rb b/lib/api/api.rb index d26667ba3f7..cb46f477ff9 100644 --- a/lib/api/api.rb +++ b/lib/api/api.rb @@ -2,6 +2,7 @@ Dir["#{Rails.root}/lib/api/*.rb"].each {|file| require file} module API class API < Grape::API + include APIGuard version 'v3', using: :path rescue_from ActiveRecord::RecordNotFound do diff --git a/lib/api/api_guard.rb b/lib/api/api_guard.rb new file mode 100644 index 00000000000..23975518181 --- /dev/null +++ b/lib/api/api_guard.rb @@ -0,0 +1,175 @@ +# Guard API with OAuth 2.0 Access Token + +require 'rack/oauth2' + +module APIGuard + extend ActiveSupport::Concern + + included do |base| + # OAuth2 Resource Server Authentication + use Rack::OAuth2::Server::Resource::Bearer, 'The API' do |request| + # The authenticator only fetches the raw token string + + # Must yield access token to store it in the env + request.access_token + end + + helpers HelperMethods + + install_error_responders(base) + end + + # Helper Methods for Grape Endpoint + module HelperMethods + # Invokes the doorkeeper guard. + # + # If token is presented and valid, then it sets @current_user. + # + # If the token does not have sufficient scopes to cover the requred scopes, + # then it raises InsufficientScopeError. + # + # If the token is expired, then it raises ExpiredError. + # + # If the token is revoked, then it raises RevokedError. + # + # If the token is not found (nil), then it raises TokenNotFoundError. + # + # Arguments: + # + # scopes: (optional) scopes required for this guard. + # Defaults to empty array. + # + def doorkeeper_guard!(scopes: []) + if (access_token = find_access_token).nil? + raise TokenNotFoundError + + else + case validate_access_token(access_token, scopes) + when Oauth2::AccessTokenValidationService::INSUFFICIENT_SCOPE + raise InsufficientScopeError.new(scopes) + + when Oauth2::AccessTokenValidationService::EXPIRED + raise ExpiredError + + when Oauth2::AccessTokenValidationService::REVOKED + raise RevokedError + + when Oauth2::AccessTokenValidationService::VALID + @current_user = User.find(access_token.resource_owner_id) + + end + end + end + + def doorkeeper_guard(scopes: []) + if access_token = find_access_token + case validate_access_token(access_token, scopes) + when Oauth2::AccessTokenValidationService::INSUFFICIENT_SCOPE + raise InsufficientScopeError.new(scopes) + + when Oauth2::AccessTokenValidationService::EXPIRED + raise ExpiredError + + when Oauth2::AccessTokenValidationService::REVOKED + raise RevokedError + + when Oauth2::AccessTokenValidationService::VALID + @current_user = User.find(access_token.resource_owner_id) + end + end + end + + def current_user + @current_user + end + + private + def find_access_token + @access_token ||= Doorkeeper.authenticate(doorkeeper_request, Doorkeeper.configuration.access_token_methods) + end + + def doorkeeper_request + @doorkeeper_request ||= ActionDispatch::Request.new(env) + end + + def validate_access_token(access_token, scopes) + Oauth2::AccessTokenValidationService.validate(access_token, scopes: scopes) + end + end + + module ClassMethods + # Installs the doorkeeper guard on the whole Grape API endpoint. + # + # Arguments: + # + # scopes: (optional) scopes required for this guard. + # Defaults to empty array. + # + def guard_all!(scopes: []) + before do + guard! scopes: scopes + end + end + + private + def install_error_responders(base) + error_classes = [ MissingTokenError, TokenNotFoundError, + ExpiredError, RevokedError, InsufficientScopeError] + + base.send :rescue_from, *error_classes, oauth2_bearer_token_error_handler + end + + def oauth2_bearer_token_error_handler + Proc.new {|e| + response = case e + when MissingTokenError + Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new + + when TokenNotFoundError + Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new( + :invalid_token, + "Bad Access Token.") + + when ExpiredError + Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new( + :invalid_token, + "Token is expired. You can either do re-authorization or token refresh.") + + when RevokedError + Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new( + :invalid_token, + "Token was revoked. You have to re-authorize from the user.") + + when InsufficientScopeError + # FIXME: ForbiddenError (inherited from Bearer::Forbidden of Rack::Oauth2) + # does not include WWW-Authenticate header, which breaks the standard. + Rack::OAuth2::Server::Resource::Bearer::Forbidden.new( + :insufficient_scope, + Rack::OAuth2::Server::Resource::ErrorMethods::DEFAULT_DESCRIPTION[:insufficient_scope], + { :scope => e.scopes}) + end + + response.finish + } + end + end + + # + # Exceptions + # + + class MissingTokenError < StandardError; end + + class TokenNotFoundError < StandardError; end + + class ExpiredError < StandardError; end + + class RevokedError < StandardError; end + + class InsufficientScopeError < StandardError + attr_reader :scopes + def initialize(scopes) + @scopes = scopes + end + end +end
\ No newline at end of file diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb index 027fb20ec46..2f2342840fd 100644 --- a/lib/api/helpers.rb +++ b/lib/api/helpers.rb @@ -11,7 +11,7 @@ module API def current_user private_token = (params[PRIVATE_TOKEN_PARAM] || env[PRIVATE_TOKEN_HEADER]).to_s - @current_user ||= User.find_by(authentication_token: private_token) + @current_user ||= (User.find_by(authentication_token: private_token) || doorkeeper_guard) unless @current_user && Gitlab::UserAccess.allowed?(@current_user) return nil diff --git a/lib/gitlab/theme.rb b/lib/gitlab/theme.rb index b7c50cb734d..a7c83a880f6 100644 --- a/lib/gitlab/theme.rb +++ b/lib/gitlab/theme.rb @@ -19,5 +19,19 @@ module Gitlab return themes[id] end + + def self.type_css_class_by_id(id) + types = { + BASIC => 'light_theme', + MARS => 'dark_theme', + MODERN => 'dark_theme', + GRAY => 'dark_theme', + COLOR => 'dark_theme' + } + + id ||= Gitlab.config.gitlab.default_theme + + types[id] + end end end diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake index 7ff23a7600a..43115915de1 100644 --- a/lib/tasks/gitlab/check.rake +++ b/lib/tasks/gitlab/check.rake @@ -585,10 +585,6 @@ namespace :gitlab do def gitlab_shell_patch_version Gitlab::Shell.version_required.split('.')[2].to_i end - - def has_gitlab_shell3? - gitlab_shell_version.try(:start_with?, "v3.") - end end @@ -790,14 +786,14 @@ namespace :gitlab do end def sanitized_message(project) - if sanitize + if should_sanitize? "#{project.namespace_id.to_s.yellow}/#{project.id.to_s.yellow} ... " else "#{project.name_with_namespace.yellow} ... " end end - def sanitize + def should_sanitize? if ENV['SANITIZE'] == "true" true else diff --git a/spec/features/admin/admin_hooks_spec.rb b/spec/features/admin/admin_hooks_spec.rb index b557567bd04..37d6b416d22 100644 --- a/spec/features/admin/admin_hooks_spec.rb +++ b/spec/features/admin/admin_hooks_spec.rb @@ -12,7 +12,7 @@ describe "Admin::Hooks", feature: true do describe "GET /admin/hooks" do it "should be ok" do visit admin_root_path - within ".main-nav" do + within ".sidebar-wrapper" do click_on "Hooks" end current_path.should == admin_hooks_path diff --git a/spec/requests/api/api_helpers_spec.rb b/spec/requests/api/api_helpers_spec.rb index e2f222c0d34..cc071342d7c 100644 --- a/spec/requests/api/api_helpers_spec.rb +++ b/spec/requests/api/api_helpers_spec.rb @@ -41,6 +41,7 @@ describe API, api: true do describe ".current_user" do it "should return nil for an invalid token" do env[API::APIHelpers::PRIVATE_TOKEN_HEADER] = 'invalid token' + self.class.any_instance.stub(:doorkeeper_guard){ false } current_user.should be_nil end diff --git a/spec/requests/api/doorkeeper_access_spec.rb b/spec/requests/api/doorkeeper_access_spec.rb new file mode 100644 index 00000000000..ddef99d77af --- /dev/null +++ b/spec/requests/api/doorkeeper_access_spec.rb @@ -0,0 +1,31 @@ +require 'spec_helper' + +describe API::API, api: true do + include ApiHelpers + + let!(:user) { create(:user) } + let!(:application) { Doorkeeper::Application.create!(:name => "MyApp", :redirect_uri => "https://app.com", :owner => user) } + let!(:token) { Doorkeeper::AccessToken.create! :application_id => application.id, :resource_owner_id => user.id } + + + describe "when unauthenticated" do + it "returns authentication success" do + get api("/user"), :access_token => token.token + response.status.should == 200 + end + end + + describe "when token invalid" do + it "returns authentication error" do + get api("/user"), :access_token => "123a" + response.status.should == 401 + end + end + + describe "authorization by private token" do + it "returns authentication success" do + get api("/user", user) + response.status.should == 200 + end + end +end diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 2c4b68c10b6..f8c5d40b9bf 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -198,8 +198,6 @@ describe API::API, api: true do it 'should respond with 400 on failure' do post api("/projects/user/#{user.id}", admin) response.status.should == 400 - json_response['message']['creator'].should == ['can\'t be blank'] - json_response['message']['namespace'].should == ['can\'t be blank'] json_response['message']['name'].should == [ 'can\'t be blank', 'is too short (minimum is 0 characters)', diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb index e6505040317..f149f3f62a9 100644 --- a/spec/routing/project_routing_spec.rb +++ b/spec/routing/project_routing_spec.rb @@ -430,6 +430,26 @@ describe Projects::TreeController, "routing" do end end +describe Projects::EditTreeController, 'routing' do + it 'to #show' do + get('/gitlab/gitlabhq/edit/master/app/models/project.rb').should( + route_to('projects/edit_tree#show', + project_id: 'gitlab/gitlabhq', + id: 'master/app/models/project.rb')) + get('/gitlab/gitlabhq/edit/master/app/models/project.rb/preview').should( + route_to('projects/edit_tree#show', + project_id: 'gitlab/gitlabhq', + id: 'master/app/models/project.rb/preview')) + end + + it 'to #preview' do + post('/gitlab/gitlabhq/edit/master/app/models/project.rb/preview').should( + route_to('projects/edit_tree#preview', + project_id: 'gitlab/gitlabhq', + id: 'master/app/models/project.rb')) + end +end + # project_compare_index GET /:project_id/compare(.:format) compare#index {id: /[^\/]+/, project_id: /[^\/]+/} # POST /:project_id/compare(.:format) compare#create {id: /[^\/]+/, project_id: /[^\/]+/} # project_compare /:project_id/compare/:from...:to(.:format) compare#show {from: /.+/, to: /.+/, id: /[^\/]+/, project_id: /[^\/]+/} |