Commit 906c65b6243e1f55f96d58cc1d4d60dd64c5cc65
Exists in
master
and in
4 other branches
Merge branch 'master' of https://github.com/gitlabhq/gitlabhq
Conflicts: config/application.rb config/gitlab.yml.example config/unicorn.rb.example
Showing
303 changed files
with
4882 additions
and
1336 deletions
Show diff stats
.travis.yml
| @@ -9,7 +9,6 @@ env: | @@ -9,7 +9,6 @@ env: | ||
| 9 | - TASK=jasmine:ci | 9 | - TASK=jasmine:ci |
| 10 | before_install: | 10 | before_install: |
| 11 | - sudo apt-get install libicu-dev -y | 11 | - sudo apt-get install libicu-dev -y |
| 12 | - - gem install charlock_holmes -v="0.6.9" | ||
| 13 | branches: | 12 | branches: |
| 14 | only: | 13 | only: |
| 15 | - 'master' | 14 | - 'master' |
| @@ -17,9 +16,12 @@ rvm: | @@ -17,9 +16,12 @@ rvm: | ||
| 17 | - 2.0.0 | 16 | - 2.0.0 |
| 18 | services: | 17 | services: |
| 19 | - mysql | 18 | - mysql |
| 19 | + - redis-server | ||
| 20 | before_script: | 20 | before_script: |
| 21 | - "cp config/database.yml.$DB config/database.yml" | 21 | - "cp config/database.yml.$DB config/database.yml" |
| 22 | - "cp config/gitlab.yml.example config/gitlab.yml" | 22 | - "cp config/gitlab.yml.example config/gitlab.yml" |
| 23 | - "bundle exec rake db:setup" | 23 | - "bundle exec rake db:setup" |
| 24 | - "bundle exec rake db:seed_fu" | 24 | - "bundle exec rake db:seed_fu" |
| 25 | script: "bundle exec rake $TASK --trace" | 25 | script: "bundle exec rake $TASK --trace" |
| 26 | +notifications: | ||
| 27 | + email: false |
CHANGELOG
| 1 | +v 6.4.0 | ||
| 2 | + - Added sorting to project issues page (Jason Blanchard) | ||
| 3 | + - Assembla integration (Carlos Paramio) | ||
| 4 | + - Fixed another 500 error with submodules | ||
| 5 | + - UI: More compact issues page | ||
| 6 | + - Minimal password length increased to 8 symbols | ||
| 7 | + - Side-by-side diff view (Steven Thonus) | ||
| 8 | + - Internal projects (Jason Hollingsworth) | ||
| 9 | + - Allow removal of avatar (Drew Blessing) | ||
| 10 | + - Project web hooks now support issues and merge request events | ||
| 11 | + - Visiting project page while not logged in will redirect to sign-in instead of 404 (Jason Hollingsworth) | ||
| 12 | + | ||
| 1 | v 6.3.0 | 13 | v 6.3.0 |
| 2 | - API for adding gitlab-ci service | 14 | - API for adding gitlab-ci service |
| 3 | - Init script now waits for pids to appear after (re)starting before reporting status (Rovanion Luckey) | 15 | - Init script now waits for pids to appear after (re)starting before reporting status (Rovanion Luckey) |
| @@ -9,6 +21,34 @@ v 6.3.0 | @@ -9,6 +21,34 @@ v 6.3.0 | ||
| 9 | - Fixed issue with 500 error when group did not exist | 21 | - Fixed issue with 500 error when group did not exist |
| 10 | - Ability to leave project | 22 | - Ability to leave project |
| 11 | - You can create file in repo using UI | 23 | - You can create file in repo using UI |
| 24 | + - You can remove file from repo using UI | ||
| 25 | + - API: dropped default_branch attribute from project during creation | ||
| 26 | + - Project default_branch is not stored in db any more. It takes from repo now. | ||
| 27 | + - Admin broadcast messages | ||
| 28 | + - UI improvements | ||
| 29 | + - Dont show last push widget if user removed this branch | ||
| 30 | + - Fix 500 error for repos with newline in file name | ||
| 31 | + - Extended html titles | ||
| 32 | + - API: create/update/delete repo files | ||
| 33 | + - Admin can transfer project to any namespace | ||
| 34 | + - API: projects/all for admin users | ||
| 35 | + - Fix recent branches order | ||
| 36 | + | ||
| 37 | +v 6.2.4 | ||
| 38 | + - Security: Cast API private_token to string (CVE-2013-4580) | ||
| 39 | + - Security: Require gitlab-shell 1.7.8 (CVE-2013-4581, CVE-2013-4582, CVE-2013-4583) | ||
| 40 | + - Fix for Git SSH access for LDAP users | ||
| 41 | + | ||
| 42 | +v 6.2.3 | ||
| 43 | + - Security: More protection against CVE-2013-4489 | ||
| 44 | + - Security: Require gitlab-shell 1.7.4 (CVE-2013-4490, CVE-2013-4546) | ||
| 45 | + - Fix sidekiq rake tasks | ||
| 46 | + | ||
| 47 | +v 6.2.2 | ||
| 48 | + - Security: Update gitlab_git (CVE-2013-4489) | ||
| 49 | + | ||
| 50 | +v 6.2.1 | ||
| 51 | + - Security: Fix issue with generated passwords for new users | ||
| 12 | 52 | ||
| 13 | v 6.2.0 | 53 | v 6.2.0 |
| 14 | - Public project pages are now visible to everyone (files, issues, wik, etc.) | 54 | - Public project pages are now visible to everyone (files, issues, wik, etc.) |
| @@ -30,7 +70,7 @@ v 6.2.0 | @@ -30,7 +70,7 @@ v 6.2.0 | ||
| 30 | - Avatar upload on profile page with a maximum of 100KB (Steven Thonus) | 70 | - Avatar upload on profile page with a maximum of 100KB (Steven Thonus) |
| 31 | - Store the sessions in Redis instead of the cookie store | 71 | - Store the sessions in Redis instead of the cookie store |
| 32 | - Fixed relative links in markdown | 72 | - Fixed relative links in markdown |
| 33 | - - User must confirm his email if signup enabled | 73 | + - User must confirm their email if signup enabled |
| 34 | - User must confirm changed email | 74 | - User must confirm changed email |
| 35 | 75 | ||
| 36 | v 6.1.0 | 76 | v 6.1.0 |
| @@ -52,7 +92,7 @@ v 6.1.0 | @@ -52,7 +92,7 @@ v 6.1.0 | ||
| 52 | - Add links to create branch/tag from project home page | 92 | - Add links to create branch/tag from project home page |
| 53 | - Add public-project? checkbox to new-project view | 93 | - Add public-project? checkbox to new-project view |
| 54 | - Improved compare page. Added link to proceed into Merge Request | 94 | - Improved compare page. Added link to proceed into Merge Request |
| 55 | - - Send email to user when he was added to group | 95 | + - Send an email to a user when they are added to group |
| 56 | - New landing page when you have 0 projects | 96 | - New landing page when you have 0 projects |
| 57 | 97 | ||
| 58 | v 6.0.0 | 98 | v 6.0.0 |
| @@ -95,6 +135,14 @@ v 6.0.0 | @@ -95,6 +135,14 @@ v 6.0.0 | ||
| 95 | - Improved MR comments logic | 135 | - Improved MR comments logic |
| 96 | - Render readme file for projects in public area | 136 | - Render readme file for projects in public area |
| 97 | 137 | ||
| 138 | +v 5.4.2 | ||
| 139 | + - Security: Cast API private_token to string (CVE-2013-4580) | ||
| 140 | + - Security: Require gitlab-shell 1.7.8 (CVE-2013-4581, CVE-2013-4582, CVE-2013-4583) | ||
| 141 | + | ||
| 142 | +v 5.4.1 | ||
| 143 | + - Security: Fixes for CVE-2013-4489 | ||
| 144 | + - Security: Require gitlab-shell 1.7.4 (CVE-2013-4490, CVE-2013-4546) | ||
| 145 | + | ||
| 98 | v 5.4.0 | 146 | v 5.4.0 |
| 99 | - Ability to edit own comments | 147 | - Ability to edit own comments |
| 100 | - Documentation improvements | 148 | - Documentation improvements |
CONTRIBUTING.md
| @@ -9,6 +9,14 @@ This guide details how to use issues and pull requests to improve GitLab. | @@ -9,6 +9,14 @@ This guide details how to use issues and pull requests to improve GitLab. | ||
| 9 | 9 | ||
| 10 | If you want to know how the GitLab team handles contributions have a look at [the GitLab contributing process](PROCESS.md). | 10 | If you want to know how the GitLab team handles contributions have a look at [the GitLab contributing process](PROCESS.md). |
| 11 | 11 | ||
| 12 | +## Contributor license agreement | ||
| 13 | + | ||
| 14 | +By submitting code as an individual you agree to the [individual contributor license agreement](doc/legal/individual_contributor_license_agreement.md). By submitting code as an entity you agree to the [corporate contributor license agreement](doc/legal/corporate_contributor_license_agreement.md). | ||
| 15 | + | ||
| 16 | +## Security vulnerability disclosure | ||
| 17 | + | ||
| 18 | +Please report suspected security vulnerabilities in private to support@gitlab.com, also see the [disclosure section on the GitLab.com website](http://www.gitlab.com/disclosure/). Please do NOT create publicly viewable issues for suspected security vulnerabilities. | ||
| 19 | + | ||
| 12 | ## Closing policy for issues and pull requests | 20 | ## Closing policy for issues and pull requests |
| 13 | 21 | ||
| 14 | GitLab is a popular open source project and the capacity to deal with issues and pull requests is limited. Out of respect for our volunteers, issues and pull requests not in line with the guidelines listed in this document may be closed without notice. | 22 | GitLab is a popular open source project and the capacity to deal with issues and pull requests is limited. Out of respect for our volunteers, issues and pull requests not in line with the guidelines listed in this document may be closed without notice. |
| @@ -74,6 +82,3 @@ We will accept pull requests if: | @@ -74,6 +82,3 @@ We will accept pull requests if: | ||
| 74 | * It is a single commit (please use `git rebase -i` to squash commits) | 82 | * It is a single commit (please use `git rebase -i` to squash commits) |
| 75 | 83 | ||
| 76 | For examples of feedback on pull requests please look at already [closed pull requests](https://github.com/gitlabhq/gitlabhq/pulls?direction=desc&page=1&sort=created&state=closed). | 84 | For examples of feedback on pull requests please look at already [closed pull requests](https://github.com/gitlabhq/gitlabhq/pulls?direction=desc&page=1&sort=created&state=closed). |
| 77 | - | ||
| 78 | -## Security vulnerabilities | ||
| 79 | -Please report security vulnerabilities in private to support@gitlab.com; also see http://www.gitlab.com/disclosure/. Do NOT create GitHub issues for security vulnerabilities. |
Gemfile
| @@ -8,7 +8,7 @@ def linux_only(require_as) | @@ -8,7 +8,7 @@ def linux_only(require_as) | ||
| 8 | RUBY_PLATFORM.include?('linux') && require_as | 8 | RUBY_PLATFORM.include?('linux') && require_as |
| 9 | end | 9 | end |
| 10 | 10 | ||
| 11 | -gem "rails", "3.2.15" | 11 | +gem "rails", "3.2.16" |
| 12 | 12 | ||
| 13 | # Supported DBs | 13 | # Supported DBs |
| 14 | gem "mysql2", group: :mysql | 14 | gem "mysql2", group: :mysql |
| @@ -24,26 +24,27 @@ gem 'omniauth-github' | @@ -24,26 +24,27 @@ gem 'omniauth-github' | ||
| 24 | 24 | ||
| 25 | # Extracting information from a git repository | 25 | # Extracting information from a git repository |
| 26 | # Provide access to Gitlab::Git library | 26 | # Provide access to Gitlab::Git library |
| 27 | -gem "gitlab_git", "~> 3.0.0.rc2" | 27 | +gem "gitlab_git", "~> 3.1.0" |
| 28 | 28 | ||
| 29 | # Ruby/Rack Git Smart-HTTP Server Handler | 29 | # Ruby/Rack Git Smart-HTTP Server Handler |
| 30 | -gem 'gitlab-grack', '~> 1.0.1', require: 'grack' | 30 | +gem 'gitlab-grack', '~> 1.1.0', require: 'grack' |
| 31 | 31 | ||
| 32 | # LDAP Auth | 32 | # LDAP Auth |
| 33 | gem 'gitlab_omniauth-ldap', '1.0.3', require: "omniauth-ldap" | 33 | gem 'gitlab_omniauth-ldap', '1.0.3', require: "omniauth-ldap" |
| 34 | 34 | ||
| 35 | # Syntax highlighter | 35 | # Syntax highlighter |
| 36 | -gem "gitlab-pygments.rb", '~> 0.3.2', require: 'pygments.rb' | 36 | +gem "gitlab-pygments.rb", '~> 0.5.4', require: 'pygments.rb' |
| 37 | 37 | ||
| 38 | # Git Wiki | 38 | # Git Wiki |
| 39 | -gem "gitlab-gollum-lib", "~> 1.0.1", require: 'gollum-lib' | 39 | +gem "gitlab-gollum-lib", "~> 1.0.2", require: 'gollum-lib' |
| 40 | 40 | ||
| 41 | # Language detection | 41 | # Language detection |
| 42 | -gem "github-linguist", require: "linguist" | 42 | +gem "gitlab-linguist", "~> 2.9.6", require: "linguist" |
| 43 | 43 | ||
| 44 | # API | 44 | # API |
| 45 | gem "grape", "~> 0.4.1" | 45 | gem "grape", "~> 0.4.1" |
| 46 | gem "grape-entity", "~> 0.3.0" | 46 | gem "grape-entity", "~> 0.3.0" |
| 47 | +gem 'rack-cors', require: 'rack/cors' | ||
| 47 | 48 | ||
| 48 | # Format dates and times | 49 | # Format dates and times |
| 49 | # based on human-friendly examples | 50 | # based on human-friendly examples |
| @@ -135,7 +136,7 @@ group :assets do | @@ -135,7 +136,7 @@ group :assets do | ||
| 135 | gem 'turbolinks' | 136 | gem 'turbolinks' |
| 136 | gem 'jquery-turbolinks' | 137 | gem 'jquery-turbolinks' |
| 137 | 138 | ||
| 138 | - gem 'chosen-rails', "1.0.0" | 139 | + gem 'chosen-rails', "1.0.1" |
| 139 | gem 'select2-rails' | 140 | gem 'select2-rails' |
| 140 | gem 'jquery-atwho-rails', "0.3.0" | 141 | gem 'jquery-atwho-rails', "0.3.0" |
| 141 | gem "jquery-rails", "2.1.3" | 142 | gem "jquery-rails", "2.1.3" |
Gemfile.lock
| 1 | GEM | 1 | GEM |
| 2 | remote: https://rubygems.org/ | 2 | remote: https://rubygems.org/ |
| 3 | specs: | 3 | specs: |
| 4 | - actionmailer (3.2.15) | ||
| 5 | - actionpack (= 3.2.15) | 4 | + actionmailer (3.2.16) |
| 5 | + actionpack (= 3.2.16) | ||
| 6 | mail (~> 2.5.4) | 6 | mail (~> 2.5.4) |
| 7 | - actionpack (3.2.15) | ||
| 8 | - activemodel (= 3.2.15) | ||
| 9 | - activesupport (= 3.2.15) | 7 | + actionpack (3.2.16) |
| 8 | + activemodel (= 3.2.16) | ||
| 9 | + activesupport (= 3.2.16) | ||
| 10 | builder (~> 3.0.0) | 10 | builder (~> 3.0.0) |
| 11 | erubis (~> 2.7.0) | 11 | erubis (~> 2.7.0) |
| 12 | journey (~> 1.0.4) | 12 | journey (~> 1.0.4) |
| @@ -14,18 +14,18 @@ GEM | @@ -14,18 +14,18 @@ GEM | ||
| 14 | rack-cache (~> 1.2) | 14 | rack-cache (~> 1.2) |
| 15 | rack-test (~> 0.6.1) | 15 | rack-test (~> 0.6.1) |
| 16 | sprockets (~> 2.2.1) | 16 | sprockets (~> 2.2.1) |
| 17 | - activemodel (3.2.15) | ||
| 18 | - activesupport (= 3.2.15) | 17 | + activemodel (3.2.16) |
| 18 | + activesupport (= 3.2.16) | ||
| 19 | builder (~> 3.0.0) | 19 | builder (~> 3.0.0) |
| 20 | - activerecord (3.2.15) | ||
| 21 | - activemodel (= 3.2.15) | ||
| 22 | - activesupport (= 3.2.15) | 20 | + activerecord (3.2.16) |
| 21 | + activemodel (= 3.2.16) | ||
| 22 | + activesupport (= 3.2.16) | ||
| 23 | arel (~> 3.0.2) | 23 | arel (~> 3.0.2) |
| 24 | tzinfo (~> 0.3.29) | 24 | tzinfo (~> 0.3.29) |
| 25 | - activeresource (3.2.15) | ||
| 26 | - activemodel (= 3.2.15) | ||
| 27 | - activesupport (= 3.2.15) | ||
| 28 | - activesupport (3.2.15) | 25 | + activeresource (3.2.16) |
| 26 | + activemodel (= 3.2.16) | ||
| 27 | + activesupport (= 3.2.16) | ||
| 28 | + activesupport (3.2.16) | ||
| 29 | i18n (~> 0.6, >= 0.6.4) | 29 | i18n (~> 0.6, >= 0.6.4) |
| 30 | multi_json (~> 1.0) | 30 | multi_json (~> 1.0) |
| 31 | acts-as-taggable-on (2.4.1) | 31 | acts-as-taggable-on (2.4.1) |
| @@ -34,11 +34,11 @@ GEM | @@ -34,11 +34,11 @@ GEM | ||
| 34 | annotate (2.6.0.beta2) | 34 | annotate (2.6.0.beta2) |
| 35 | activerecord (>= 2.3.0) | 35 | activerecord (>= 2.3.0) |
| 36 | rake (>= 0.8.7) | 36 | rake (>= 0.8.7) |
| 37 | - arel (3.0.2) | 37 | + arel (3.0.3) |
| 38 | asciidoctor (0.1.3) | 38 | asciidoctor (0.1.3) |
| 39 | awesome_print (1.2.0) | 39 | awesome_print (1.2.0) |
| 40 | backports (3.3.2) | 40 | backports (3.3.2) |
| 41 | - bcrypt-ruby (3.1.1) | 41 | + bcrypt-ruby (3.1.2) |
| 42 | better_errors (1.0.1) | 42 | better_errors (1.0.1) |
| 43 | coderay (>= 1.0.0) | 43 | coderay (>= 1.0.0) |
| 44 | erubis (>= 2.6.6) | 44 | erubis (>= 2.6.6) |
| @@ -61,12 +61,12 @@ GEM | @@ -61,12 +61,12 @@ GEM | ||
| 61 | charlock_holmes (0.6.9.4) | 61 | charlock_holmes (0.6.9.4) |
| 62 | childprocess (0.3.9) | 62 | childprocess (0.3.9) |
| 63 | ffi (~> 1.0, >= 1.0.11) | 63 | ffi (~> 1.0, >= 1.0.11) |
| 64 | - chosen-rails (1.0.0) | 64 | + chosen-rails (1.0.1) |
| 65 | coffee-rails (>= 3.2) | 65 | coffee-rails (>= 3.2) |
| 66 | compass-rails (>= 1.0) | 66 | compass-rails (>= 1.0) |
| 67 | railties (>= 3.0) | 67 | railties (>= 3.0) |
| 68 | sass-rails (>= 3.2) | 68 | sass-rails (>= 3.2) |
| 69 | - chunky_png (1.2.8) | 69 | + chunky_png (1.2.9) |
| 70 | cliver (0.2.1) | 70 | cliver (0.2.1) |
| 71 | code_analyzer (0.4.3) | 71 | code_analyzer (0.4.3) |
| 72 | sexp_processor | 72 | sexp_processor |
| @@ -77,7 +77,7 @@ GEM | @@ -77,7 +77,7 @@ GEM | ||
| 77 | coffee-script (2.2.0) | 77 | coffee-script (2.2.0) |
| 78 | coffee-script-source | 78 | coffee-script-source |
| 79 | execjs | 79 | execjs |
| 80 | - coffee-script-source (1.6.2) | 80 | + coffee-script-source (1.6.3) |
| 81 | colored (1.2) | 81 | colored (1.2) |
| 82 | colorize (0.5.8) | 82 | colorize (0.5.8) |
| 83 | compass (0.12.2) | 83 | compass (0.12.2) |
| @@ -101,14 +101,14 @@ GEM | @@ -101,14 +101,14 @@ GEM | ||
| 101 | database_cleaner (1.1.1) | 101 | database_cleaner (1.1.1) |
| 102 | debug_inspector (0.0.2) | 102 | debug_inspector (0.0.2) |
| 103 | descendants_tracker (0.0.1) | 103 | descendants_tracker (0.0.1) |
| 104 | - devise (2.2.5) | 104 | + devise (2.2.8) |
| 105 | bcrypt-ruby (~> 3.0) | 105 | bcrypt-ruby (~> 3.0) |
| 106 | orm_adapter (~> 0.1) | 106 | orm_adapter (~> 0.1) |
| 107 | railties (~> 3.1) | 107 | railties (~> 3.1) |
| 108 | warden (~> 1.2.1) | 108 | warden (~> 1.2.1) |
| 109 | devise-async (0.8.0) | 109 | devise-async (0.8.0) |
| 110 | devise (>= 2.2, < 3.2) | 110 | devise (>= 2.2, < 3.2) |
| 111 | - diff-lcs (1.2.4) | 111 | + diff-lcs (1.2.5) |
| 112 | dotenv (0.8.0) | 112 | dotenv (0.8.0) |
| 113 | email_spec (1.4.0) | 113 | email_spec (1.4.0) |
| 114 | launchy (~> 2.1) | 114 | launchy (~> 2.1) |
| @@ -119,8 +119,7 @@ GEM | @@ -119,8 +119,7 @@ GEM | ||
| 119 | escape_utils (0.2.4) | 119 | escape_utils (0.2.4) |
| 120 | eventmachine (1.0.3) | 120 | eventmachine (1.0.3) |
| 121 | excon (0.13.4) | 121 | excon (0.13.4) |
| 122 | - execjs (1.4.0) | ||
| 123 | - multi_json (~> 1.0) | 122 | + execjs (2.0.2) |
| 124 | factory_girl (4.2.0) | 123 | factory_girl (4.2.0) |
| 125 | activesupport (>= 3.0.0) | 124 | activesupport (>= 3.0.0) |
| 126 | factory_girl_rails (4.2.1) | 125 | factory_girl_rails (4.2.1) |
| @@ -151,38 +150,39 @@ GEM | @@ -151,38 +150,39 @@ GEM | ||
| 151 | fssm (0.2.10) | 150 | fssm (0.2.10) |
| 152 | gemoji (1.2.1) | 151 | gemoji (1.2.1) |
| 153 | gherkin-ruby (0.3.0) | 152 | gherkin-ruby (0.3.0) |
| 154 | - github-linguist (2.3.4) | ||
| 155 | - charlock_holmes (~> 0.6.6) | ||
| 156 | - escape_utils (~> 0.2.3) | ||
| 157 | - mime-types (~> 1.19) | ||
| 158 | - pygments.rb (>= 0.2.13) | ||
| 159 | - github-markdown (0.5.3) | 153 | + github-markdown (0.5.5) |
| 160 | github-markup (0.7.5) | 154 | github-markup (0.7.5) |
| 161 | gitlab-flowdock-git-hook (0.4.2.2) | 155 | gitlab-flowdock-git-hook (0.4.2.2) |
| 162 | gitlab-grit (>= 2.4.1) | 156 | gitlab-grit (>= 2.4.1) |
| 163 | multi_json | 157 | multi_json |
| 164 | - gitlab-gollum-lib (1.0.1) | 158 | + gitlab-gollum-lib (1.0.2) |
| 165 | github-markdown (~> 0.5.3) | 159 | github-markdown (~> 0.5.3) |
| 166 | github-markup (>= 0.7.5, < 1.0.0) | 160 | github-markup (>= 0.7.5, < 1.0.0) |
| 167 | - gitlab-grit (>= 2.5.1) | 161 | + gitlab-grit (~> 2.6.1) |
| 162 | + gitlab-pygments.rb (~> 0.5.4) | ||
| 168 | nokogiri (~> 1.5.9) | 163 | nokogiri (~> 1.5.9) |
| 169 | - pygments.rb (~> 0.4.2) | ||
| 170 | sanitize (~> 2.0.3) | 164 | sanitize (~> 2.0.3) |
| 171 | stringex (~> 1.5.1) | 165 | stringex (~> 1.5.1) |
| 172 | - gitlab-grack (1.0.1) | 166 | + gitlab-grack (1.1.0) |
| 173 | rack (~> 1.4.1) | 167 | rack (~> 1.4.1) |
| 174 | - gitlab-grit (2.6.1) | 168 | + gitlab-grit (2.6.3) |
| 175 | charlock_holmes (~> 0.6.9) | 169 | charlock_holmes (~> 0.6.9) |
| 176 | diff-lcs (~> 1.1) | 170 | diff-lcs (~> 1.1) |
| 177 | mime-types (~> 1.15) | 171 | mime-types (~> 1.15) |
| 178 | posix-spawn (~> 0.3.6) | 172 | posix-spawn (~> 0.3.6) |
| 179 | - gitlab-pygments.rb (0.3.2) | 173 | + gitlab-linguist (2.9.6) |
| 174 | + charlock_holmes (~> 0.6.6) | ||
| 175 | + escape_utils (~> 0.2.4) | ||
| 176 | + gitlab-pygments.rb (~> 0.5.4) | ||
| 177 | + mime-types (~> 1.19) | ||
| 178 | + gitlab-pygments.rb (0.5.4) | ||
| 180 | posix-spawn (~> 0.3.6) | 179 | posix-spawn (~> 0.3.6) |
| 181 | yajl-ruby (~> 1.1.0) | 180 | yajl-ruby (~> 1.1.0) |
| 182 | - gitlab_git (3.0.0.rc2) | 181 | + gitlab_git (3.1.0) |
| 183 | activesupport (~> 3.2.13) | 182 | activesupport (~> 3.2.13) |
| 184 | - github-linguist (~> 2.3.4) | ||
| 185 | gitlab-grit (~> 2.6.1) | 183 | gitlab-grit (~> 2.6.1) |
| 184 | + gitlab-linguist (~> 2.9.5) | ||
| 185 | + gitlab-pygments.rb (~> 0.5.4) | ||
| 186 | gitlab_meta (6.0) | 186 | gitlab_meta (6.0) |
| 187 | gitlab_omniauth-ldap (1.0.3) | 187 | gitlab_omniauth-ldap (1.0.3) |
| 188 | net-ldap (~> 0.3.1) | 188 | net-ldap (~> 0.3.1) |
| @@ -235,7 +235,7 @@ GEM | @@ -235,7 +235,7 @@ GEM | ||
| 235 | multi_json (~> 1.0) | 235 | multi_json (~> 1.0) |
| 236 | multi_xml (>= 0.5.2) | 236 | multi_xml (>= 0.5.2) |
| 237 | httpauth (0.2.0) | 237 | httpauth (0.2.0) |
| 238 | - i18n (0.6.5) | 238 | + i18n (0.6.9) |
| 239 | jasmine (1.3.2) | 239 | jasmine (1.3.2) |
| 240 | jasmine-core (~> 1.3.1) | 240 | jasmine-core (~> 1.3.1) |
| 241 | rack (~> 1.0) | 241 | rack (~> 1.0) |
| @@ -274,7 +274,7 @@ GEM | @@ -274,7 +274,7 @@ GEM | ||
| 274 | mime-types (~> 1.16) | 274 | mime-types (~> 1.16) |
| 275 | treetop (~> 1.4.8) | 275 | treetop (~> 1.4.8) |
| 276 | method_source (0.8.1) | 276 | method_source (0.8.1) |
| 277 | - mime-types (1.25) | 277 | + mime-types (1.25.1) |
| 278 | minitest (4.7.4) | 278 | minitest (4.7.4) |
| 279 | modernizr (2.6.2) | 279 | modernizr (2.6.2) |
| 280 | sprockets (~> 2.0) | 280 | sprockets (~> 2.0) |
| @@ -312,7 +312,7 @@ GEM | @@ -312,7 +312,7 @@ GEM | ||
| 312 | omniauth-twitter (0.0.17) | 312 | omniauth-twitter (0.0.17) |
| 313 | multi_json (~> 1.3) | 313 | multi_json (~> 1.3) |
| 314 | omniauth-oauth (~> 1.0) | 314 | omniauth-oauth (~> 1.0) |
| 315 | - orm_adapter (0.4.0) | 315 | + orm_adapter (0.5.0) |
| 316 | pg (0.15.1) | 316 | pg (0.15.1) |
| 317 | poltergeist (1.4.1) | 317 | poltergeist (1.4.1) |
| 318 | capybara (~> 2.1.0) | 318 | capybara (~> 2.1.0) |
| @@ -325,9 +325,6 @@ GEM | @@ -325,9 +325,6 @@ GEM | ||
| 325 | coderay (~> 1.0.5) | 325 | coderay (~> 1.0.5) |
| 326 | method_source (~> 0.8) | 326 | method_source (~> 0.8) |
| 327 | slop (~> 3.4) | 327 | slop (~> 3.4) |
| 328 | - pygments.rb (0.4.2) | ||
| 329 | - posix-spawn (~> 0.3.6) | ||
| 330 | - yajl-ruby (~> 1.1.0) | ||
| 331 | pyu-ruby-sasl (0.0.3.3) | 328 | pyu-ruby-sasl (0.0.3.3) |
| 332 | quiet_assets (1.0.2) | 329 | quiet_assets (1.0.2) |
| 333 | railties (>= 3.1, < 5.0) | 330 | railties (>= 3.1, < 5.0) |
| @@ -338,6 +335,7 @@ GEM | @@ -338,6 +335,7 @@ GEM | ||
| 338 | rack | 335 | rack |
| 339 | rack-cache (1.2) | 336 | rack-cache (1.2) |
| 340 | rack (>= 0.4) | 337 | rack (>= 0.4) |
| 338 | + rack-cors (0.2.9) | ||
| 341 | rack-mini-profiler (0.1.31) | 339 | rack-mini-profiler (0.1.31) |
| 342 | rack (>= 1.1.3) | 340 | rack (>= 1.1.3) |
| 343 | rack-mount (0.8.3) | 341 | rack-mount (0.8.3) |
| @@ -348,14 +346,14 @@ GEM | @@ -348,14 +346,14 @@ GEM | ||
| 348 | rack | 346 | rack |
| 349 | rack-test (0.6.2) | 347 | rack-test (0.6.2) |
| 350 | rack (>= 1.0) | 348 | rack (>= 1.0) |
| 351 | - rails (3.2.15) | ||
| 352 | - actionmailer (= 3.2.15) | ||
| 353 | - actionpack (= 3.2.15) | ||
| 354 | - activerecord (= 3.2.15) | ||
| 355 | - activeresource (= 3.2.15) | ||
| 356 | - activesupport (= 3.2.15) | 349 | + rails (3.2.16) |
| 350 | + actionmailer (= 3.2.16) | ||
| 351 | + actionpack (= 3.2.16) | ||
| 352 | + activerecord (= 3.2.16) | ||
| 353 | + activeresource (= 3.2.16) | ||
| 354 | + activesupport (= 3.2.16) | ||
| 357 | bundler (~> 1.0) | 355 | bundler (~> 1.0) |
| 358 | - railties (= 3.2.15) | 356 | + railties (= 3.2.16) |
| 359 | rails-dev-tweaks (0.6.1) | 357 | rails-dev-tweaks (0.6.1) |
| 360 | actionpack (~> 3.1) | 358 | actionpack (~> 3.1) |
| 361 | railties (~> 3.1) | 359 | railties (~> 3.1) |
| @@ -368,9 +366,9 @@ GEM | @@ -368,9 +366,9 @@ GEM | ||
| 368 | i18n | 366 | i18n |
| 369 | require_all | 367 | require_all |
| 370 | ruby-progressbar | 368 | ruby-progressbar |
| 371 | - railties (3.2.15) | ||
| 372 | - actionpack (= 3.2.15) | ||
| 373 | - activesupport (= 3.2.15) | 369 | + railties (3.2.16) |
| 370 | + actionpack (= 3.2.16) | ||
| 371 | + activesupport (= 3.2.16) | ||
| 374 | rack-ssl (~> 1.3.2) | 372 | rack-ssl (~> 1.3.2) |
| 375 | rake (>= 0.8.7) | 373 | rake (>= 0.8.7) |
| 376 | rdoc (~> 3.4) | 374 | rdoc (~> 3.4) |
| @@ -431,7 +429,7 @@ GEM | @@ -431,7 +429,7 @@ GEM | ||
| 431 | safe_yaml (0.9.3) | 429 | safe_yaml (0.9.3) |
| 432 | sanitize (2.0.3) | 430 | sanitize (2.0.3) |
| 433 | nokogiri (>= 1.4.4, < 1.6) | 431 | nokogiri (>= 1.4.4, < 1.6) |
| 434 | - sass (3.2.11) | 432 | + sass (3.2.12) |
| 435 | sass-rails (3.2.6) | 433 | sass-rails (3.2.6) |
| 436 | railties (~> 3.2.0) | 434 | railties (~> 3.2.0) |
| 437 | sass (>= 3.1.10) | 435 | sass (>= 3.1.10) |
| @@ -559,7 +557,7 @@ DEPENDENCIES | @@ -559,7 +557,7 @@ DEPENDENCIES | ||
| 559 | bootstrap-sass | 557 | bootstrap-sass |
| 560 | capybara | 558 | capybara |
| 561 | carrierwave | 559 | carrierwave |
| 562 | - chosen-rails (= 1.0.0) | 560 | + chosen-rails (= 1.0.1) |
| 563 | coffee-rails | 561 | coffee-rails |
| 564 | colored | 562 | colored |
| 565 | coveralls | 563 | coveralls |
| @@ -575,13 +573,13 @@ DEPENDENCIES | @@ -575,13 +573,13 @@ DEPENDENCIES | ||
| 575 | font-awesome-rails | 573 | font-awesome-rails |
| 576 | foreman | 574 | foreman |
| 577 | gemoji (~> 1.2.1) | 575 | gemoji (~> 1.2.1) |
| 578 | - github-linguist | ||
| 579 | github-markup (~> 0.7.4) | 576 | github-markup (~> 0.7.4) |
| 580 | gitlab-flowdock-git-hook (~> 0.4.2) | 577 | gitlab-flowdock-git-hook (~> 0.4.2) |
| 581 | - gitlab-gollum-lib (~> 1.0.1) | ||
| 582 | - gitlab-grack (~> 1.0.1) | ||
| 583 | - gitlab-pygments.rb (~> 0.3.2) | ||
| 584 | - gitlab_git (~> 3.0.0.rc2) | 578 | + gitlab-gollum-lib (~> 1.0.2) |
| 579 | + gitlab-grack (~> 1.1.0) | ||
| 580 | + gitlab-linguist (~> 2.9.6) | ||
| 581 | + gitlab-pygments.rb (~> 0.5.4) | ||
| 582 | + gitlab_git (~> 3.1.0) | ||
| 585 | gitlab_meta (= 6.0) | 583 | gitlab_meta (= 6.0) |
| 586 | gitlab_omniauth-ldap (= 1.0.3) | 584 | gitlab_omniauth-ldap (= 1.0.3) |
| 587 | gon | 585 | gon |
| @@ -613,8 +611,9 @@ DEPENDENCIES | @@ -613,8 +611,9 @@ DEPENDENCIES | ||
| 613 | pry | 611 | pry |
| 614 | quiet_assets (~> 1.0.1) | 612 | quiet_assets (~> 1.0.1) |
| 615 | rack-attack | 613 | rack-attack |
| 614 | + rack-cors | ||
| 616 | rack-mini-profiler | 615 | rack-mini-profiler |
| 617 | - rails (= 3.2.15) | 616 | + rails (= 3.2.16) |
| 618 | rails-dev-tweaks | 617 | rails-dev-tweaks |
| 619 | rails_best_practices | 618 | rails_best_practices |
| 620 | raphael-rails (~> 2.1.2) | 619 | raphael-rails (~> 2.1.2) |
README.md
| @@ -32,7 +32,9 @@ | @@ -32,7 +32,9 @@ | ||
| 32 | 32 | ||
| 33 | * GitLab.com commercial services: [Homepage](http://www.gitlab.com/) | [Subscription](http://www.gitlab.com/subscription/) | [Consultancy](http://www.gitlab.com/consultancy/) | [GitLab Cloud](http://www.gitlab.com/cloud/) | [Blog](http://blog.gitlab.com/) | 33 | * GitLab.com commercial services: [Homepage](http://www.gitlab.com/) | [Subscription](http://www.gitlab.com/subscription/) | [Consultancy](http://www.gitlab.com/consultancy/) | [GitLab Cloud](http://www.gitlab.com/cloud/) | [Blog](http://blog.gitlab.com/) |
| 34 | 34 | ||
| 35 | -* GitLab CI: [Readme](https://github.com/gitlabhq/gitlab-ci/blob/master/README.md) of the GitLab open-source continuous integration server | 35 | +* [GitLab Enterprise Edition](https://www.gitlab.com/features/) offers additional features that are useful for larger organizations (100+ users). |
| 36 | + | ||
| 37 | +* [GitLab CI](https://github.com/gitlabhq/gitlab-ci/blob/master/README.md) is a continuous integration (CI) server that is easy to integrate with GitLab. | ||
| 36 | 38 | ||
| 37 | ### Requirements | 39 | ### Requirements |
| 38 | 40 | ||
| @@ -44,31 +46,24 @@ | @@ -44,31 +46,24 @@ | ||
| 44 | 46 | ||
| 45 | ** More details are in the [requirements doc](doc/install/requirements.md) | 47 | ** More details are in the [requirements doc](doc/install/requirements.md) |
| 46 | 48 | ||
| 47 | -### Installation | ||
| 48 | - | ||
| 49 | -#### Official production installation | ||
| 50 | - | ||
| 51 | -* [Installation guide for a production server](doc/install/installation.md) | 49 | +### Official installation methods |
| 52 | 50 | ||
| 51 | +* [Manual installation guide for a production server](doc/install/installation.md) | ||
| 53 | 52 | ||
| 54 | -#### Official development installation | 53 | +* [GitLab Chef Cookbook](https://gitlab.com/gitlab-org/cookbook-gitlab/blob/master/README.md) This cookbook can be used both for development installations and production installations. If you want to [contribute](CONTRIBUTE.md) to GitLab we suggest you follow the [development installation on a virtual machine with Vagrant](https://gitlab.com/gitlab-org/cookbook-gitlab/blob/master/doc/development.md) instructions to install all testing dependencies. |
| 55 | 54 | ||
| 56 | -If you want to contribute, please first read our [Contributing Guidelines](https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md) and then we suggest you to use the Vagrant virtual machine project to get an environment working with all dependencies. | 55 | +### Third party one-click installers |
| 57 | 56 | ||
| 58 | -* [Vagrant virtual machine for development](https://github.com/gitlabhq/gitlab-vagrant-vm) | 57 | +* [Digital Ocean 1-Click Application Install](https://www.digitalocean.com/blog_posts/host-your-git-repositories-in-55-seconds-with-gitlab) Have a new server up in 55 seconds. Digital Ocean uses SSD disks which is great for an IO intensive app such as GitLab. |
| 59 | 58 | ||
| 59 | +* [BitNami one-click installers](http://bitnami.com/stack/gitlab) This package contains both GitLab and GitLab CI. It is available as installer, virtual machine or for cloud hosting providers (Amazon Web Services/Azure/etc.). | ||
| 60 | 60 | ||
| 61 | -#### Unofficial production installations | 61 | +#### Unofficial installation methods |
| 62 | 62 | ||
| 63 | * [GitLab recipes](https://github.com/gitlabhq/gitlab-recipes) repository with unofficial guides for using GitLab with different software (operating systems, webservers, etc.) than the official version. | 63 | * [GitLab recipes](https://github.com/gitlabhq/gitlab-recipes) repository with unofficial guides for using GitLab with different software (operating systems, webservers, etc.) than the official version. |
| 64 | 64 | ||
| 65 | * [Installation guides](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Unofficial-Installation-Guides) public wiki with unofficial guides to install GitLab on different operating systems. | 65 | * [Installation guides](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Unofficial-Installation-Guides) public wiki with unofficial guides to install GitLab on different operating systems. |
| 66 | 66 | ||
| 67 | -* [BitNami one-click installers](http://bitnami.com/stack/gitlab) | ||
| 68 | - | ||
| 69 | -* [TurnKey Linux virtual appliance](http://www.turnkeylinux.org/gitlab) | ||
| 70 | - | ||
| 71 | - | ||
| 72 | ### New versions and upgrading | 67 | ### New versions and upgrading |
| 73 | 68 | ||
| 74 | Since 2011 GitLab is released on the 22nd of every month. Every new release includes an upgrade guide. | 69 | Since 2011 GitLab is released on the 22nd of every month. Every new release includes an upgrade guide. |
| @@ -79,7 +74,6 @@ Since 2011 GitLab is released on the 22nd of every month. Every new release incl | @@ -79,7 +74,6 @@ Since 2011 GitLab is released on the 22nd of every month. Every new release incl | ||
| 79 | 74 | ||
| 80 | * Features that will be in the next releases are listed on [the feedback and suggestions forum](http://feedback.gitlab.com/forums/176466-general) with the status [started](http://feedback.gitlab.com/forums/176466-general/status/796456) and [completed](http://feedback.gitlab.com/forums/176466-general/status/796457). | 75 | * Features that will be in the next releases are listed on [the feedback and suggestions forum](http://feedback.gitlab.com/forums/176466-general) with the status [started](http://feedback.gitlab.com/forums/176466-general/status/796456) and [completed](http://feedback.gitlab.com/forums/176466-general/status/796457). |
| 81 | 76 | ||
| 82 | - | ||
| 83 | ### Run in production mode | 77 | ### Run in production mode |
| 84 | 78 | ||
| 85 | 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: | 79 | 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: |
| @@ -110,7 +104,7 @@ or start each component separately | @@ -110,7 +104,7 @@ or start each component separately | ||
| 110 | 104 | ||
| 111 | * Run all tests | 105 | * Run all tests |
| 112 | 106 | ||
| 113 | - bundle exec rake gitlab:test | 107 | + bundle exec rake gitlab:test RAILS_ENV=test |
| 114 | 108 | ||
| 115 | * [RSpec](http://rspec.info/) unit and functional tests | 109 | * [RSpec](http://rspec.info/) unit and functional tests |
| 116 | 110 | ||
| @@ -147,15 +141,17 @@ or start each component separately | @@ -147,15 +141,17 @@ or start each component separately | ||
| 147 | 141 | ||
| 148 | * [Mailing list](https://groups.google.com/forum/#!forum/gitlabhq) and [Stack Overflow](http://stackoverflow.com/questions/tagged/gitlab) are the best places to ask questions. For example you can use it if you have questions about: permission denied errors, invisible repos, can't clone/pull/push or with web hooks that don't fire. Please search for similar issues before posting your own, there's a good chance somebody else had the same issue you have now and has resolved it. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there to a fix. | 142 | * [Mailing list](https://groups.google.com/forum/#!forum/gitlabhq) and [Stack Overflow](http://stackoverflow.com/questions/tagged/gitlab) are the best places to ask questions. For example you can use it if you have questions about: permission denied errors, invisible repos, can't clone/pull/push or with web hooks that don't fire. Please search for similar issues before posting your own, there's a good chance somebody else had the same issue you have now and has resolved it. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there to a fix. |
| 149 | 143 | ||
| 150 | -* [Unofficial #gitlab IRC on Freenode](http://www.freenode.net/) is another way to get in touch with other GitLab users who may be able to help you. | ||
| 151 | - | ||
| 152 | * [Feedback and suggestions forum](http://feedback.gitlab.com) is the place to propose and discuss new features for GitLab. | 144 | * [Feedback and suggestions forum](http://feedback.gitlab.com) is the place to propose and discuss new features for GitLab. |
| 153 | 145 | ||
| 154 | * [Contributing guide](https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md) describes how to submit pull requests and issues. Pull requests and issues not in line with the guidelines in this document will be closed. | 146 | * [Contributing guide](https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md) describes how to submit pull requests and issues. Pull requests and issues not in line with the guidelines in this document will be closed. |
| 155 | 147 | ||
| 156 | * [Support subscription](http://www.gitlab.com/subscription/) connects you to the knowledge of GitLab experts that will resolve your issues and answer your questions. | 148 | * [Support subscription](http://www.gitlab.com/subscription/) connects you to the knowledge of GitLab experts that will resolve your issues and answer your questions. |
| 157 | 149 | ||
| 158 | -* [Consultancy](http://www.gitlab.com/consultancy/) allows you hire GitLab experts for installations, upgrades and customizations. | 150 | +* [Consultancy](http://www.gitlab.com/consultancy/) from the GitLab experts for installations, upgrades and customizations. |
| 151 | + | ||
| 152 | +* [#gitlab IRC channel](http://www.freenode.net/) on Freenode is unofficial but offers a way to get in touch with other GitLab users who may be able to help you. | ||
| 153 | + | ||
| 154 | +* [Book](http://www.packtpub.com/gitlab-repository-management/book) written by GitLab enthusiast Jonathan M. Hethey is unofficial but it offers a good overview. | ||
| 159 | 155 | ||
| 160 | 156 | ||
| 161 | ### Getting in touch | 157 | ### Getting in touch |
VERSION
app/assets/images/favicon.ico
No preview for this file type
app/assets/images/logo-black.png
app/assets/images/logo-white.png
app/assets/javascripts/api.js.coffee
| @@ -2,6 +2,7 @@ | @@ -2,6 +2,7 @@ | ||
| 2 | users_path: "/api/:version/users.json" | 2 | users_path: "/api/:version/users.json" |
| 3 | user_path: "/api/:version/users/:id.json" | 3 | user_path: "/api/:version/users/:id.json" |
| 4 | notes_path: "/api/:version/projects/:id/notes.json" | 4 | notes_path: "/api/:version/projects/:id/notes.json" |
| 5 | + namespaces_path: "/api/:version/namespaces.json" | ||
| 5 | 6 | ||
| 6 | # Get 20 (depends on api) recent notes | 7 | # Get 20 (depends on api) recent notes |
| 7 | # and sort the ascending from oldest to newest | 8 | # and sort the ascending from oldest to newest |
| @@ -49,6 +50,20 @@ | @@ -49,6 +50,20 @@ | ||
| 49 | ).done (users) -> | 50 | ).done (users) -> |
| 50 | callback(users) | 51 | callback(users) |
| 51 | 52 | ||
| 53 | + # Return namespaces list. Filtered by query | ||
| 54 | + namespaces: (query, callback) -> | ||
| 55 | + url = Api.buildUrl(Api.namespaces_path) | ||
| 56 | + | ||
| 57 | + $.ajax( | ||
| 58 | + url: url | ||
| 59 | + data: | ||
| 60 | + private_token: gon.api_token | ||
| 61 | + search: query | ||
| 62 | + per_page: 20 | ||
| 63 | + dataType: "json" | ||
| 64 | + ).done (namespaces) -> | ||
| 65 | + callback(namespaces) | ||
| 66 | + | ||
| 52 | buildUrl: (url) -> | 67 | buildUrl: (url) -> |
| 53 | url = gon.relative_url_root + url if gon.relative_url_root? | 68 | url = gon.relative_url_root + url if gon.relative_url_root? |
| 54 | return url.replace(':version', gon.api_version) | 69 | return url.replace(':version', gon.api_version) |
app/assets/javascripts/blob.js.coffee
| 1 | class BlobView | 1 | class BlobView |
| 2 | constructor: -> | 2 | constructor: -> |
| 3 | + # handle multi-line select | ||
| 4 | + handleMultiSelect = (e) -> | ||
| 5 | + [ first_line, last_line ] = parseSelectedLines() | ||
| 6 | + [ line_number ] = parseSelectedLines($(this).attr("id")) | ||
| 7 | + hash = "L#{line_number}" | ||
| 8 | + | ||
| 9 | + if e.shiftKey and not isNaN(first_line) and not isNaN(line_number) | ||
| 10 | + if line_number < first_line | ||
| 11 | + last_line = first_line | ||
| 12 | + first_line = line_number | ||
| 13 | + else | ||
| 14 | + last_line = line_number | ||
| 15 | + | ||
| 16 | + hash = if first_line == last_line then "L#{first_line}" else "L#{first_line}-#{last_line}" | ||
| 17 | + | ||
| 18 | + setHash(hash) | ||
| 19 | + e.preventDefault() | ||
| 20 | + | ||
| 3 | # See if there are lines selected | 21 | # See if there are lines selected |
| 4 | # "#L12" and "#L34-56" supported | 22 | # "#L12" and "#L34-56" supported |
| 5 | - highlightBlobLines = -> | ||
| 6 | - if window.location.hash isnt "" | ||
| 7 | - matches = window.location.hash.match(/\#L(\d+)(\-(\d+))?/) | 23 | + highlightBlobLines = (e) -> |
| 24 | + [ first_line, last_line ] = parseSelectedLines() | ||
| 25 | + | ||
| 26 | + unless isNaN first_line | ||
| 27 | + $("#tree-content-holder .highlight .line").removeClass("hll") | ||
| 28 | + $("#LC#{line}").addClass("hll") for line in [first_line..last_line] | ||
| 29 | + $("#L#{first_line}").ScrollTo() unless e? | ||
| 30 | + | ||
| 31 | + # parse selected lines from hash | ||
| 32 | + # always return first and last line (initialized to NaN) | ||
| 33 | + parseSelectedLines = (str) -> | ||
| 34 | + first_line = NaN | ||
| 35 | + last_line = NaN | ||
| 36 | + hash = str || window.location.hash | ||
| 37 | + | ||
| 38 | + if hash isnt "" | ||
| 39 | + matches = hash.match(/\#?L(\d+)(\-(\d+))?/) | ||
| 8 | first_line = parseInt(matches?[1]) | 40 | first_line = parseInt(matches?[1]) |
| 9 | last_line = parseInt(matches?[3]) | 41 | last_line = parseInt(matches?[3]) |
| 42 | + last_line = first_line if isNaN(last_line) | ||
| 43 | + | ||
| 44 | + [ first_line, last_line ] | ||
| 45 | + | ||
| 46 | + setHash = (hash) -> | ||
| 47 | + hash = hash.replace(/^\#/, "") | ||
| 48 | + nodes = $("#" + hash) | ||
| 49 | + # if any nodes are using this id, they must be temporarily changed | ||
| 50 | + # also, add a temporary div at the top of the screen to prevent scrolling | ||
| 51 | + if nodes.length > 0 | ||
| 52 | + scroll_top = $(document).scrollTop() | ||
| 53 | + nodes.attr("id", "") | ||
| 54 | + tmp = $("<div></div>") | ||
| 55 | + .css({ position: "absolute", visibility: "hidden", top: scroll_top + "px" }) | ||
| 56 | + .attr("id", hash) | ||
| 57 | + .appendTo(document.body) | ||
| 58 | + | ||
| 59 | + window.location.hash = hash | ||
| 60 | + | ||
| 61 | + # restore the nodes | ||
| 62 | + if nodes.length > 0 | ||
| 63 | + tmp.remove() | ||
| 64 | + nodes.attr("id", hash) | ||
| 10 | 65 | ||
| 11 | - unless isNaN first_line | ||
| 12 | - last_line = first_line if isNaN(last_line) | ||
| 13 | - $("#tree-content-holder .highlight .line").removeClass("hll") | ||
| 14 | - $("#LC#{line}").addClass("hll") for line in [first_line..last_line] | ||
| 15 | - $("#L#{first_line}").ScrollTo() | 66 | + # initialize multi-line select |
| 67 | + $("#tree-content-holder .line_numbers a[id^=L]").on("click", handleMultiSelect) | ||
| 16 | 68 | ||
| 17 | # Highlight the correct lines on load | 69 | # Highlight the correct lines on load |
| 18 | highlightBlobLines() | 70 | highlightBlobLines() |
| 19 | 71 | ||
| 20 | # Highlight the correct lines when the hash part of the URL changes | 72 | # Highlight the correct lines when the hash part of the URL changes |
| 21 | - $(window).on 'hashchange', highlightBlobLines | 73 | + $(window).on("hashchange", highlightBlobLines) |
| 22 | 74 | ||
| 23 | 75 | ||
| 24 | @BlobView = BlobView | 76 | @BlobView = BlobView |
app/assets/javascripts/commits.js.coffee
| @@ -4,13 +4,13 @@ class CommitsList | @@ -4,13 +4,13 @@ class CommitsList | ||
| 4 | limit: 0 | 4 | limit: 0 |
| 5 | offset: 0 | 5 | offset: 0 |
| 6 | @disable = false | 6 | @disable = false |
| 7 | - | 7 | + |
| 8 | @showProgress: -> | 8 | @showProgress: -> |
| 9 | $('.loading').show() | 9 | $('.loading').show() |
| 10 | - | 10 | + |
| 11 | @hideProgress: -> | 11 | @hideProgress: -> |
| 12 | $('.loading').hide() | 12 | $('.loading').hide() |
| 13 | - | 13 | + |
| 14 | @init: (ref, limit) -> | 14 | @init: (ref, limit) -> |
| 15 | $(".day-commits-table li.commit").live 'click', (event) -> | 15 | $(".day-commits-table li.commit").live 'click', (event) -> |
| 16 | if event.target.nodeName != "A" | 16 | if event.target.nodeName != "A" |
| @@ -21,7 +21,7 @@ class CommitsList | @@ -21,7 +21,7 @@ class CommitsList | ||
| 21 | @data.ref = ref | 21 | @data.ref = ref |
| 22 | @data.limit = limit | 22 | @data.limit = limit |
| 23 | @data.offset = limit | 23 | @data.offset = limit |
| 24 | - | 24 | + |
| 25 | this.initLoadMore() | 25 | this.initLoadMore() |
| 26 | this.showProgress() | 26 | this.showProgress() |
| 27 | 27 | ||
| @@ -32,7 +32,9 @@ class CommitsList | @@ -32,7 +32,9 @@ class CommitsList | ||
| 32 | url: location.href | 32 | url: location.href |
| 33 | data: @data | 33 | data: @data |
| 34 | complete: this.hideProgress | 34 | complete: this.hideProgress |
| 35 | - dataType: "script" | 35 | + success: (data) -> |
| 36 | + CommitsList.append(data.count, data.html) | ||
| 37 | + dataType: "json" | ||
| 36 | 38 | ||
| 37 | @append: (count, html) -> | 39 | @append: (count, html) -> |
| 38 | $("#commits-list").append(html) | 40 | $("#commits-list").append(html) |
| @@ -40,7 +42,7 @@ class CommitsList | @@ -40,7 +42,7 @@ class CommitsList | ||
| 40 | @data.offset += count | 42 | @data.offset += count |
| 41 | else | 43 | else |
| 42 | @disable = true | 44 | @disable = true |
| 43 | - | 45 | + |
| 44 | @initLoadMore: -> | 46 | @initLoadMore: -> |
| 45 | $(document).unbind('scroll') | 47 | $(document).unbind('scroll') |
| 46 | $(document).endlessScroll | 48 | $(document).endlessScroll |
app/assets/javascripts/issues.js.coffee
| @@ -22,7 +22,7 @@ | @@ -22,7 +22,7 @@ | ||
| 22 | backgroundColor: '#DDD' | 22 | backgroundColor: '#DDD' |
| 23 | opacity: .4 | 23 | opacity: .4 |
| 24 | ) | 24 | ) |
| 25 | - | 25 | + |
| 26 | reload: -> | 26 | reload: -> |
| 27 | Issues.initSelects() | 27 | Issues.initSelects() |
| 28 | Issues.initChecks() | 28 | Issues.initChecks() |
| @@ -54,7 +54,16 @@ | @@ -54,7 +54,16 @@ | ||
| 54 | unless terms is last_terms | 54 | unless terms is last_terms |
| 55 | last_terms = terms | 55 | last_terms = terms |
| 56 | if terms.length >= 2 or terms.length is 0 | 56 | if terms.length >= 2 or terms.length is 0 |
| 57 | - form.submit() | 57 | + $.ajax |
| 58 | + type: "GET" | ||
| 59 | + url: location.href | ||
| 60 | + data: "issue_search=" + terms | ||
| 61 | + complete: -> | ||
| 62 | + $(".loading").hide() | ||
| 63 | + success: (data) -> | ||
| 64 | + $('.issues-holder').html(data.html) | ||
| 65 | + Issues.reload() | ||
| 66 | + dataType: "json" | ||
| 58 | 67 | ||
| 59 | checkChanged: -> | 68 | checkChanged: -> |
| 60 | checked_issues = $(".selected_issue:checked") | 69 | checked_issues = $(".selected_issue:checked") |
app/assets/javascripts/main.js.coffee
app/assets/javascripts/merge_requests.js.coffee
| @@ -21,7 +21,7 @@ class MergeRequest | @@ -21,7 +21,7 @@ class MergeRequest | ||
| 21 | this.initMergeWidget() | 21 | this.initMergeWidget() |
| 22 | this.$('.show-all-commits').on 'click', => | 22 | this.$('.show-all-commits').on 'click', => |
| 23 | this.showAllCommits() | 23 | this.showAllCommits() |
| 24 | - | 24 | + |
| 25 | modal = $('#modal_merge_info').modal(show: false) | 25 | modal = $('#modal_merge_info').modal(show: false) |
| 26 | 26 | ||
| 27 | # Local jQuery finder | 27 | # Local jQuery finder |
| @@ -83,12 +83,12 @@ class MergeRequest | @@ -83,12 +83,12 @@ class MergeRequest | ||
| 83 | url: this.$('.nav-tabs .diffs-tab a').attr('href') | 83 | url: this.$('.nav-tabs .diffs-tab a').attr('href') |
| 84 | beforeSend: => | 84 | beforeSend: => |
| 85 | this.$('.status').addClass 'loading' | 85 | this.$('.status').addClass 'loading' |
| 86 | - | ||
| 87 | complete: => | 86 | complete: => |
| 88 | @diffs_loaded = true | 87 | @diffs_loaded = true |
| 89 | this.$('.status').removeClass 'loading' | 88 | this.$('.status').removeClass 'loading' |
| 90 | - | ||
| 91 | - dataType: 'script' | 89 | + success: (data) => |
| 90 | + this.$(".diffs").html(data.html) | ||
| 91 | + dataType: 'json' | ||
| 92 | 92 | ||
| 93 | showAllCommits: -> | 93 | showAllCommits: -> |
| 94 | this.$('.first-commits').remove() | 94 | this.$('.first-commits').remove() |
| @@ -0,0 +1,24 @@ | @@ -0,0 +1,24 @@ | ||
| 1 | +$ -> | ||
| 2 | + namespaceFormatResult = (namespace) -> | ||
| 3 | + markup = "<div class='namespace-result'>" | ||
| 4 | + markup += "<span class='namespace-kind'>" + namespace.kind + "</span>" | ||
| 5 | + markup += "<span class='namespace-path'>" + namespace.path + "</span>" | ||
| 6 | + markup += "</div>" | ||
| 7 | + markup | ||
| 8 | + | ||
| 9 | + formatSelection = (namespace) -> | ||
| 10 | + namespace.kind + ": " + namespace.path | ||
| 11 | + | ||
| 12 | + $('.ajax-namespace-select').each (i, select) -> | ||
| 13 | + $(select).select2 | ||
| 14 | + placeholder: "Search for namespace" | ||
| 15 | + multiple: $(select).hasClass('multiselect') | ||
| 16 | + minimumInputLength: 0 | ||
| 17 | + query: (query) -> | ||
| 18 | + Api.namespaces query.term, (namespaces) -> | ||
| 19 | + data = { results: namespaces } | ||
| 20 | + query.callback(data) | ||
| 21 | + | ||
| 22 | + dropdownCssClass: "ajax-namespace-dropdown" | ||
| 23 | + formatResult: namespaceFormatResult | ||
| 24 | + formatSelection: formatSelection |
app/assets/javascripts/notes.js
| @@ -6,7 +6,7 @@ var NoteList = { | @@ -6,7 +6,7 @@ var NoteList = { | ||
| 6 | target_type: null, | 6 | target_type: null, |
| 7 | 7 | ||
| 8 | init: function(tid, tt, path) { | 8 | init: function(tid, tt, path) { |
| 9 | - NoteList.notes_path = path + ".js"; | 9 | + NoteList.notes_path = path + ".json"; |
| 10 | NoteList.target_id = tid; | 10 | NoteList.target_id = tid; |
| 11 | NoteList.target_type = tt; | 11 | NoteList.target_type = tt; |
| 12 | NoteList.target_params = "target_type=" + NoteList.target_type + "&target_id=" + NoteList.target_id; | 12 | NoteList.target_params = "target_type=" + NoteList.target_type + "&target_id=" + NoteList.target_id; |
| @@ -233,10 +233,12 @@ var NoteList = { | @@ -233,10 +233,12 @@ var NoteList = { | ||
| 233 | form.show(); | 233 | form.show(); |
| 234 | 234 | ||
| 235 | var textarea = form.find("textarea"); | 235 | var textarea = form.find("textarea"); |
| 236 | - var p = $("<p></p>").text(textarea.val()); | ||
| 237 | - var hidden_div = $('<div class="note-original-content"></div>').append(p); | ||
| 238 | - form.append(hidden_div); | ||
| 239 | - hidden_div.hide(); | 236 | + if (form.find(".note-original-content").length === 0) { |
| 237 | + var p = $("<p></p>").text(textarea.val()); | ||
| 238 | + var hidden_div = $('<div class="note-original-content"></div>').append(p); | ||
| 239 | + form.append(hidden_div); | ||
| 240 | + hidden_div.hide(); | ||
| 241 | + } | ||
| 240 | textarea.focus(); | 242 | textarea.focus(); |
| 241 | }, | 243 | }, |
| 242 | 244 | ||
| @@ -409,7 +411,10 @@ var NoteList = { | @@ -409,7 +411,10 @@ var NoteList = { | ||
| 409 | data: NoteList.target_params, | 411 | data: NoteList.target_params, |
| 410 | complete: function(){ $('.js-notes-busy').removeClass("loading")}, | 412 | complete: function(){ $('.js-notes-busy').removeClass("loading")}, |
| 411 | beforeSend: function() { $('.js-notes-busy').addClass("loading") }, | 413 | beforeSend: function() { $('.js-notes-busy').addClass("loading") }, |
| 412 | - dataType: "script" | 414 | + success: function(data) { |
| 415 | + NoteList.setContent(data.html); | ||
| 416 | + }, | ||
| 417 | + dataType: "json" | ||
| 413 | }); | 418 | }); |
| 414 | }, | 419 | }, |
| 415 | 420 | ||
| @@ -417,7 +422,7 @@ var NoteList = { | @@ -417,7 +422,7 @@ var NoteList = { | ||
| 417 | * Called in response to getContent(). | 422 | * Called in response to getContent(). |
| 418 | * Replaces the content of #notes-list with the given html. | 423 | * Replaces the content of #notes-list with the given html. |
| 419 | */ | 424 | */ |
| 420 | - setContent: function(newNoteIds, html) { | 425 | + setContent: function(html) { |
| 421 | $("#notes-list").html(html); | 426 | $("#notes-list").html(html); |
| 422 | }, | 427 | }, |
| 423 | 428 | ||
| @@ -532,6 +537,8 @@ var NoteList = { | @@ -532,6 +537,8 @@ var NoteList = { | ||
| 532 | note_text.html(response.note).show(); | 537 | note_text.html(response.note).show(); |
| 533 | 538 | ||
| 534 | var note_form = note_li.find(".note-edit-form"); | 539 | var note_form = note_li.find(".note-edit-form"); |
| 540 | + var original_content = note_form.find(".note-original-content"); | ||
| 541 | + original_content.remove(); | ||
| 535 | note_form.hide(); | 542 | note_form.hide(); |
| 536 | note_form.find(".btn-save").enableButton(); | 543 | note_form.find(".btn-save").enableButton(); |
| 537 | 544 |
app/assets/javascripts/pager.js.coffee
| @@ -19,8 +19,9 @@ | @@ -19,8 +19,9 @@ | ||
| 19 | data: "limit=" + @limit + "&offset=" + @offset | 19 | data: "limit=" + @limit + "&offset=" + @offset |
| 20 | complete: -> | 20 | complete: -> |
| 21 | $(".loading").hide() | 21 | $(".loading").hide() |
| 22 | - | ||
| 23 | - dataType: "script" | 22 | + success: (data) -> |
| 23 | + Pager.append(data.count, data.html) | ||
| 24 | + dataType: "json" | ||
| 24 | 25 | ||
| 25 | append: (count, html) -> | 26 | append: (count, html) -> |
| 26 | $(".content_list").append html | 27 | $(".content_list").append html |
app/assets/javascripts/project.js.coffee
| @@ -40,3 +40,9 @@ $ -> | @@ -40,3 +40,9 @@ $ -> | ||
| 40 | # Ref switcher | 40 | # Ref switcher |
| 41 | $('.project-refs-select').on 'change', -> | 41 | $('.project-refs-select').on 'change', -> |
| 42 | $(@).parents('form').submit() | 42 | $(@).parents('form').submit() |
| 43 | + | ||
| 44 | + $('.hide-no-ssh-message').on 'click', (e) -> | ||
| 45 | + path = '/' | ||
| 46 | + $.cookie('hide_no_ssh_message', 'false', { path: path }) | ||
| 47 | + $(@).parents('.no-ssh-key-message').hide() | ||
| 48 | + e.preventDefault() |
app/assets/stylesheets/common.scss
| @@ -220,7 +220,6 @@ li.note { | @@ -220,7 +220,6 @@ li.note { | ||
| 220 | .error-message { | 220 | .error-message { |
| 221 | padding: 10px; | 221 | padding: 10px; |
| 222 | background: #C67; | 222 | background: #C67; |
| 223 | - padding-left: 20px; | ||
| 224 | margin: 0; | 223 | margin: 0; |
| 225 | color: #FFF; | 224 | color: #FFF; |
| 226 | 225 | ||
| @@ -228,8 +227,18 @@ li.note { | @@ -228,8 +227,18 @@ li.note { | ||
| 228 | color: #fff; | 227 | color: #fff; |
| 229 | text-decoration: underline; | 228 | text-decoration: underline; |
| 230 | } | 229 | } |
| 231 | - &.centered { | ||
| 232 | - text-align: center; | 230 | +} |
| 231 | + | ||
| 232 | +.no-ssh-key-message { | ||
| 233 | + padding: 10px 0; | ||
| 234 | + background: #C67; | ||
| 235 | + margin: 0; | ||
| 236 | + color: #FFF; | ||
| 237 | + text-align: center; | ||
| 238 | + | ||
| 239 | + a { | ||
| 240 | + color: #fff; | ||
| 241 | + text-decoration: underline; | ||
| 233 | } | 242 | } |
| 234 | } | 243 | } |
| 235 | 244 | ||
| @@ -341,4 +350,46 @@ table { | @@ -341,4 +350,46 @@ table { | ||
| 341 | .navbar-gitlab .navbar-inner .nav > li .btn-sign-in { | 350 | .navbar-gitlab .navbar-inner .nav > li .btn-sign-in { |
| 342 | @extend .btn-new; | 351 | @extend .btn-new; |
| 343 | padding: 5px 15px; | 352 | padding: 5px 15px; |
| 353 | + text-shadow: none; | ||
| 354 | +} | ||
| 355 | + | ||
| 356 | +.broadcast-message { | ||
| 357 | + padding: 10px; | ||
| 358 | + text-align: center; | ||
| 359 | + background: #555; | ||
| 360 | + color: #BBB; | ||
| 361 | +} | ||
| 362 | + | ||
| 363 | +.ajax-users-select { | ||
| 364 | + width: 400px; | ||
| 365 | + | ||
| 366 | + &.input-large { | ||
| 367 | + width: 210px; | ||
| 368 | + } | ||
| 369 | + | ||
| 370 | + &.input-clamp { | ||
| 371 | + max-width: 100%; | ||
| 372 | + } | ||
| 373 | +} | ||
| 374 | + | ||
| 375 | +.user-result { | ||
| 376 | + .user-image { | ||
| 377 | + float: left; | ||
| 378 | + } | ||
| 379 | + .user-name { | ||
| 380 | + } | ||
| 381 | + .user-username { | ||
| 382 | + color: #999; | ||
| 383 | + } | ||
| 384 | +} | ||
| 385 | + | ||
| 386 | +.namespace-result { | ||
| 387 | + .namespace-kind { | ||
| 388 | + color: #AAA; | ||
| 389 | + font-weight: normal; | ||
| 390 | + } | ||
| 391 | + .namespace-path { | ||
| 392 | + margin-left: 10px; | ||
| 393 | + font-weight: bolder; | ||
| 394 | + } | ||
| 344 | } | 395 | } |
app/assets/stylesheets/gitlab_bootstrap/blocks.scss
| @@ -34,9 +34,7 @@ | @@ -34,9 +34,7 @@ | ||
| 34 | &.ui-box-show { | 34 | &.ui-box-show { |
| 35 | color: #666; | 35 | color: #666; |
| 36 | margin:20px 0; | 36 | margin:20px 0; |
| 37 | - background: #FFF; | ||
| 38 | - box-shadow: inset 0 1px 0 #fff, 0 1px 5px #f1f1f1; | ||
| 39 | - @include linear-gradient(#fafafa, #f1f1f1); | 37 | + background: #FAFAFA; |
| 40 | 38 | ||
| 41 | .control-group { | 39 | .control-group { |
| 42 | margin-bottom: 0; | 40 | margin-bottom: 0; |
| @@ -44,11 +42,13 @@ | @@ -44,11 +42,13 @@ | ||
| 44 | } | 42 | } |
| 45 | 43 | ||
| 46 | &.ui-box-danger { | 44 | &.ui-box-danger { |
| 45 | + background: #f7f7f7; | ||
| 46 | + border: none; | ||
| 47 | + | ||
| 47 | .title { | 48 | .title { |
| 48 | - @include linear-gradient(#F26E5E, #bd362f); | 49 | + background: #D65; |
| 49 | color: #fff; | 50 | color: #fff; |
| 50 | text-shadow: 0 1px 1px #900; | 51 | text-shadow: 0 1px 1px #900; |
| 51 | - font-weight: bold; | ||
| 52 | } | 52 | } |
| 53 | } | 53 | } |
| 54 | 54 | ||
| @@ -98,9 +98,9 @@ | @@ -98,9 +98,9 @@ | ||
| 98 | } | 98 | } |
| 99 | 99 | ||
| 100 | .title { | 100 | .title { |
| 101 | - @include bg-gray-gradient; | ||
| 102 | - border-bottom: 1px solid #CCC; | ||
| 103 | - color: #456; | 101 | + background-color: #EEE; |
| 102 | + border-bottom: 1px solid #DDD; | ||
| 103 | + color: #666; | ||
| 104 | font-size: 16px; | 104 | font-size: 16px; |
| 105 | text-shadow: 0 1px 1px #fff; | 105 | text-shadow: 0 1px 1px #fff; |
| 106 | padding: 0 10px; | 106 | padding: 0 10px; |
app/assets/stylesheets/gitlab_bootstrap/buttons.scss
| 1 | .btn { | 1 | .btn { |
| 2 | display: inline-block; | 2 | display: inline-block; |
| 3 | - padding: 6px 12px; | ||
| 4 | margin-bottom: 0; | 3 | margin-bottom: 0; |
| 5 | - font-size: 13px; | ||
| 6 | - line-height: $baseLineHeight; | 4 | + font-weight: normal; |
| 7 | text-align: center; | 5 | text-align: center; |
| 8 | vertical-align: middle; | 6 | vertical-align: middle; |
| 9 | cursor: pointer; | 7 | cursor: pointer; |
| 10 | - border: 1px solid #BBB; | ||
| 11 | - color: $style_color; | ||
| 12 | - @include border-radius($baseBorderRadius); | ||
| 13 | - @include box-shadow(inset 0 1px 0 rgba(255,255,255,.2)); | ||
| 14 | - @include linear-gradient(#f1f1f1, #e1e1e1); | ||
| 15 | - text-shadow: 0 1px 1px #FFF; | ||
| 16 | - text-decoration: none; | 8 | + background-image: none; |
| 9 | + border: 1px solid transparent; | ||
| 10 | + white-space: nowrap; | ||
| 11 | + padding: 6px 12px; | ||
| 12 | + font-size: 13px; | ||
| 13 | + line-height: 18px; | ||
| 14 | + border-radius: 4px; | ||
| 15 | + -webkit-user-select: none; | ||
| 16 | + -moz-user-select: none; | ||
| 17 | + -ms-user-select: none; | ||
| 18 | + -o-user-select: none; | ||
| 19 | + user-select: none; | ||
| 20 | + color: #444444; | ||
| 21 | + background-color: #fff; | ||
| 22 | + border-color: #ccc; | ||
| 23 | + text-shadow: none; | ||
| 17 | 24 | ||
| 18 | &.hover, | 25 | &.hover, |
| 19 | &:hover { | 26 | &:hover { |
| 20 | - color: $style_color; | ||
| 21 | - background: #f1f1f1; | ||
| 22 | - border-color: #AAA; | 27 | + color: #444444; |
| 23 | text-decoration: none; | 28 | text-decoration: none; |
| 24 | - @include linear-gradient(#fAfAfA, #f1f1f1); | 29 | + background-color: #ebebeb; |
| 30 | + border-color: #adadad; | ||
| 25 | } | 31 | } |
| 26 | 32 | ||
| 27 | &.focus, | 33 | &.focus, |
| 28 | &:focus { | 34 | &:focus { |
| 35 | + color: #444444; | ||
| 29 | text-decoration: none; | 36 | text-decoration: none; |
| 30 | - @include box-shadow(inset 0 2px 4px rgba(0,0,0,.15)); | 37 | + outline: thin dotted #333; |
| 38 | + outline: 5px auto -webkit-focus-ring-color; | ||
| 39 | + outline-offset: -2px; | ||
| 31 | } | 40 | } |
| 32 | 41 | ||
| 33 | &.active, | 42 | &.active, |
| 34 | &:active { | 43 | &:active { |
| 35 | - background-image: none; | ||
| 36 | outline: 0; | 44 | outline: 0; |
| 37 | - text-decoration: none; | ||
| 38 | - @include box-shadow(inset 0 2px 4px rgba(0,0,0,.15)); | 45 | + background-image: none; |
| 46 | + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); | ||
| 47 | + box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); | ||
| 39 | } | 48 | } |
| 40 | 49 | ||
| 41 | &.disabled, | 50 | &.disabled, |
| 42 | &[disabled] { | 51 | &[disabled] { |
| 43 | - cursor: default; | ||
| 44 | - background-image: none; | ||
| 45 | - @include opacity(65); | ||
| 46 | - @include box-shadow(none); | 52 | + cursor: not-allowed; |
| 53 | + pointer-events: none; | ||
| 54 | + opacity: 0.65; | ||
| 55 | + filter: alpha(opacity=65); | ||
| 56 | + -webkit-box-shadow: none; | ||
| 57 | + box-shadow: none; | ||
| 47 | } | 58 | } |
| 48 | 59 | ||
| 49 | &.btn-primary { | 60 | &.btn-primary { |
| 50 | - color: #FFF; | ||
| 51 | - border-color: #189; | ||
| 52 | - text-shadow: 0 1px 1px #189; | ||
| 53 | - @include linear-gradient(#4AC, #289); | 61 | + color: #ffffff; |
| 62 | + background-color: #429bca; | ||
| 63 | + border-color: #358ebd; | ||
| 54 | 64 | ||
| 55 | &.hover, | 65 | &.hover, |
| 56 | &:hover, | 66 | &:hover, |
| 57 | &.disabled, | 67 | &.disabled, |
| 58 | &[disabled] { | 68 | &[disabled] { |
| 59 | - color: #FFF; | ||
| 60 | - background: #389; | 69 | + color: #ffffff; |
| 70 | + background-color: #3286b1; | ||
| 71 | + border-color: #286e8e; | ||
| 61 | } | 72 | } |
| 62 | } | 73 | } |
| 63 | 74 | ||
| 64 | &.btn-success { | 75 | &.btn-success { |
| 65 | - color: #FFF; | ||
| 66 | - border-color: #1A1; | ||
| 67 | - text-shadow: 0 1px 1px #FFF; | ||
| 68 | - text-shadow: 0 1px 1px #181; | ||
| 69 | - @include linear-gradient(#62C452, #51a351); | 76 | + color: #ffffff; |
| 77 | + background-color: #5cb85c; | ||
| 78 | + border-color: #4cae4c; | ||
| 70 | 79 | ||
| 71 | 80 | ||
| 72 | &.hover, | 81 | &.hover, |
| 73 | &:hover, | 82 | &:hover, |
| 74 | &.disabled, | 83 | &.disabled, |
| 75 | &[disabled] { | 84 | &[disabled] { |
| 76 | - color: #FFF; | ||
| 77 | - background: #2A2; | 85 | + color: #ffffff; |
| 86 | + background-color: #47a447; | ||
| 87 | + border-color: #398439; | ||
| 78 | } | 88 | } |
| 79 | } | 89 | } |
| 80 | 90 | ||
| 81 | &.btn-danger { | 91 | &.btn-danger { |
| 82 | - color: #FFF; | ||
| 83 | - text-shadow: 0 1px 1px #811; | ||
| 84 | - border-color: #BD362F; | ||
| 85 | - @include linear-gradient(#EE5F5B, #BD362F); | 92 | + color: #ffffff; |
| 93 | + background-color: #d9534f; | ||
| 94 | + border-color: #d43f3a; | ||
| 86 | 95 | ||
| 87 | 96 | ||
| 88 | &.hover, | 97 | &.hover, |
| 89 | &:hover, | 98 | &:hover, |
| 90 | &.disabled, | 99 | &.disabled, |
| 91 | &[disabled] { | 100 | &[disabled] { |
| 92 | - color: #FFF; | ||
| 93 | - background: #A22; | 101 | + color: #ffffff; |
| 102 | + background-color: #d2322d; | ||
| 103 | + border-color: #ac2925; | ||
| 94 | } | 104 | } |
| 95 | } | 105 | } |
| 96 | 106 |
app/assets/stylesheets/gitlab_bootstrap/common.scss
| 1 | /** COLORS **/ | 1 | /** COLORS **/ |
| 2 | .cgray { color: gray } | 2 | .cgray { color: gray } |
| 3 | +.clgray { color: #BBB } | ||
| 3 | .cred { color: #D12F19 } | 4 | .cred { color: #D12F19 } |
| 4 | .cgreen { color: #4a2 } | 5 | .cgreen { color: #4a2 } |
| 5 | .cblue { color: #29A } | 6 | .cblue { color: #29A } |
| 6 | .cblack { color: #111 } | 7 | .cblack { color: #111 } |
| 7 | .cdark { color: #444 } | 8 | .cdark { color: #444 } |
| 9 | +.camber { color: #ffc000 } | ||
| 8 | .cwhite { color: #fff!important } | 10 | .cwhite { color: #fff!important } |
| 9 | .bgred { background: #F2DEDE!important } | 11 | .bgred { background: #F2DEDE!important } |
| 10 | 12 | ||
| @@ -93,6 +95,12 @@ pre.well-pre { | @@ -93,6 +95,12 @@ pre.well-pre { | ||
| 93 | font-size: 12px; | 95 | font-size: 12px; |
| 94 | font-style: normal; | 96 | font-style: normal; |
| 95 | font-weight: normal; | 97 | font-weight: normal; |
| 98 | + | ||
| 99 | + &.label-gray { | ||
| 100 | + background-color: #eee; | ||
| 101 | + color: #999; | ||
| 102 | + text-shadow: none; | ||
| 103 | + } | ||
| 96 | } | 104 | } |
| 97 | 105 | ||
| 98 | /** Big Labels **/ | 106 | /** Big Labels **/ |
| @@ -116,3 +124,12 @@ pre.well-pre { | @@ -116,3 +124,12 @@ pre.well-pre { | ||
| 116 | color: #FFF; | 124 | color: #FFF; |
| 117 | } | 125 | } |
| 118 | } | 126 | } |
| 127 | + | ||
| 128 | +.dropdown-menu > li > a { | ||
| 129 | + text-shadow: none; | ||
| 130 | +} | ||
| 131 | + | ||
| 132 | +.dropdown-menu > li > a:hover, | ||
| 133 | +.dropdown-menu > li > a:focus { | ||
| 134 | + background: #29b; | ||
| 135 | +} |
app/assets/stylesheets/gitlab_bootstrap/files.scss
| @@ -11,8 +11,8 @@ | @@ -11,8 +11,8 @@ | ||
| 11 | } | 11 | } |
| 12 | 12 | ||
| 13 | .file-title { | 13 | .file-title { |
| 14 | - border-bottom: 1px solid #bbb; | ||
| 15 | - @include bg-dark-gray-gradient; | 14 | + background: #DDD; |
| 15 | + border-bottom: 1px solid #CCC; | ||
| 16 | text-shadow: 0 1px 1px #fff; | 16 | text-shadow: 0 1px 1px #fff; |
| 17 | margin: 0; | 17 | margin: 0; |
| 18 | font-weight: normal; | 18 | font-weight: normal; |
app/assets/stylesheets/gitlab_bootstrap/forms.scss
| @@ -3,6 +3,23 @@ form { | @@ -3,6 +3,23 @@ form { | ||
| 3 | 3 | ||
| 4 | label { | 4 | label { |
| 5 | @extend .control-label; | 5 | @extend .control-label; |
| 6 | + | ||
| 7 | + &.radio-label { | ||
| 8 | + text-align: left; | ||
| 9 | + width: 100%; | ||
| 10 | + margin-left: 0; | ||
| 11 | + | ||
| 12 | + input[type="radio"] { | ||
| 13 | + margin-top: 1px !important; | ||
| 14 | + } | ||
| 15 | + } | ||
| 16 | + | ||
| 17 | + &.list-label { | ||
| 18 | + float: none; | ||
| 19 | + padding: 0 !important; | ||
| 20 | + margin: 0; | ||
| 21 | + text-align: left; | ||
| 22 | + } | ||
| 6 | } | 23 | } |
| 7 | } | 24 | } |
| 8 | 25 | ||
| @@ -49,3 +66,9 @@ fieldset legend { | @@ -49,3 +66,9 @@ fieldset legend { | ||
| 49 | font-size: 16px; | 66 | font-size: 16px; |
| 50 | margin-bottom: 10px; | 67 | margin-bottom: 10px; |
| 51 | } | 68 | } |
| 69 | + | ||
| 70 | +.datetime-controls { | ||
| 71 | + select { | ||
| 72 | + width: 100px; | ||
| 73 | + } | ||
| 74 | +} |
app/assets/stylesheets/gitlab_bootstrap/mixins.scss
| @@ -89,10 +89,26 @@ | @@ -89,10 +89,26 @@ | ||
| 89 | } | 89 | } |
| 90 | 90 | ||
| 91 | code { padding: 0 4px; } | 91 | code { padding: 0 4px; } |
| 92 | - h1 { margin-top: 30px;} | ||
| 93 | - h2 { margin-top: 25px;} | ||
| 94 | - h3 { margin-top: 20px;} | ||
| 95 | - h4 { margin-top: 15px;} | 92 | + |
| 93 | + h1 { | ||
| 94 | + margin-top: 45px; | ||
| 95 | + font-size: 2.5em; | ||
| 96 | + } | ||
| 97 | + | ||
| 98 | + h2 { | ||
| 99 | + margin-top: 40px; | ||
| 100 | + font-size: 2em; | ||
| 101 | + } | ||
| 102 | + | ||
| 103 | + h3 { | ||
| 104 | + margin-top: 35px; | ||
| 105 | + font-size: 2em; | ||
| 106 | + } | ||
| 107 | + | ||
| 108 | + h4 { | ||
| 109 | + margin-top: 30px; | ||
| 110 | + font-size: 1.5em; | ||
| 111 | + } | ||
| 96 | 112 | ||
| 97 | blockquote p { | 113 | blockquote p { |
| 98 | color: #888; | 114 | color: #888; |
| @@ -107,6 +123,16 @@ | @@ -107,6 +123,16 @@ | ||
| 107 | background: #EEE; | 123 | background: #EEE; |
| 108 | } | 124 | } |
| 109 | } | 125 | } |
| 126 | + | ||
| 127 | + code { | ||
| 128 | + font-size: inherit; | ||
| 129 | + font-weight: inherit; | ||
| 130 | + color: #555; | ||
| 131 | + } | ||
| 132 | + | ||
| 133 | + li { | ||
| 134 | + line-height: 1.5; | ||
| 135 | + } | ||
| 110 | } | 136 | } |
| 111 | 137 | ||
| 112 | @mixin page-title { | 138 | @mixin page-title { |
app/assets/stylesheets/gitlab_bootstrap/nav.scss
| @@ -15,18 +15,16 @@ | @@ -15,18 +15,16 @@ | ||
| 15 | > li > a { | 15 | > li > a { |
| 16 | border-left: 4px solid #EEE; | 16 | border-left: 4px solid #EEE; |
| 17 | padding: 12px; | 17 | padding: 12px; |
| 18 | + color: #777; | ||
| 18 | } | 19 | } |
| 19 | > .active > a { | 20 | > .active > a { |
| 20 | border-color: $primary_color; | 21 | border-color: $primary_color; |
| 21 | - border-radius: 0; | ||
| 22 | - background: #F1F1F1; | ||
| 23 | - color: $style_color; | ||
| 24 | - font-weight: bold; | ||
| 25 | - text-shadow: 0 1px 1px #fff; | 22 | + background: none; |
| 23 | + color: #333; | ||
| 24 | + font-weight: bolder; | ||
| 26 | } | 25 | } |
| 27 | 26 | ||
| 28 | &.nav-stacked-menu { | 27 | &.nav-stacked-menu { |
| 29 | - background: #FAFAFA; | ||
| 30 | li > a { | 28 | li > a { |
| 31 | padding: 16px; | 29 | padding: 16px; |
| 32 | } | 30 | } |
| @@ -36,6 +34,7 @@ | @@ -36,6 +34,7 @@ | ||
| 36 | &.nav-pills-small { | 34 | &.nav-pills-small { |
| 37 | > li > a { | 35 | > li > a { |
| 38 | padding: 8px 12px; | 36 | padding: 8px 12px; |
| 37 | + font-size: 12px; | ||
| 39 | } | 38 | } |
| 40 | } | 39 | } |
| 41 | } | 40 | } |
app/assets/stylesheets/sections/admin.scss
| @@ -20,4 +20,19 @@ | @@ -20,4 +20,19 @@ | ||
| 20 | label { width: 110px; } | 20 | label { width: 110px; } |
| 21 | .controls { margin-left: 130px; } | 21 | .controls { margin-left: 130px; } |
| 22 | .form-actions { padding-left: 130px; background: #fff } | 22 | .form-actions { padding-left: 130px; background: #fff } |
| 23 | + .visibility-levels { | ||
| 24 | + .controls { | ||
| 25 | + margin-bottom: 9px; | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | + i { | ||
| 29 | + color: inherit; | ||
| 30 | + } | ||
| 31 | + } | ||
| 32 | +} | ||
| 33 | + | ||
| 34 | +.broadcast-messages { | ||
| 35 | + .message { | ||
| 36 | + line-height: 2; | ||
| 37 | + } | ||
| 23 | } | 38 | } |
app/assets/stylesheets/sections/commits.scss
| @@ -16,37 +16,29 @@ | @@ -16,37 +16,29 @@ | ||
| 16 | 16 | ||
| 17 | .header { | 17 | .header { |
| 18 | @extend .clearfix; | 18 | @extend .clearfix; |
| 19 | + background: #DDD; | ||
| 20 | + border-bottom: 1px solid #CCC; | ||
| 19 | padding: 5px 5px 5px 10px; | 21 | padding: 5px 5px 5px 10px; |
| 20 | color: #555; | 22 | color: #555; |
| 21 | - border-bottom: 1px solid #CCC; | ||
| 22 | - background: #eee; | ||
| 23 | - // TODO Replace with linear-gradient mixin | ||
| 24 | - background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, #eee), to(#dfdfdf)); | ||
| 25 | - background-image: -webkit-linear-gradient(#eee 6.6%, #dfdfdf); | ||
| 26 | - background-image: -moz-linear-gradient(#eee 6.6%, #dfdfdf); | ||
| 27 | - background-image: -ms-linear-gradient(#eee 6.6%, #dfdfdf); | ||
| 28 | - background-image: -o-linear-gradient(#eee 6.6%, #dfdfdf); | ||
| 29 | - | ||
| 30 | - a{ | ||
| 31 | - color: $style_color; | ||
| 32 | - } | ||
| 33 | 23 | ||
| 34 | > span { | 24 | > span { |
| 35 | font-family: $monospace_font; | 25 | font-family: $monospace_font; |
| 36 | font-size: 14px; | 26 | font-size: 14px; |
| 37 | - line-height: 30px; | 27 | + line-height: 2; |
| 38 | } | 28 | } |
| 39 | 29 | ||
| 40 | - a.view-file{ | 30 | + .view-file { |
| 41 | font-weight: bold; | 31 | font-weight: bold; |
| 32 | + float: right; | ||
| 33 | + background-color: #EEE; | ||
| 42 | } | 34 | } |
| 43 | 35 | ||
| 44 | - .commit-short-id{ | 36 | + .commit-short-id { |
| 45 | font-family: $monospace_font; | 37 | font-family: $monospace_font; |
| 46 | font-size: smaller; | 38 | font-size: smaller; |
| 47 | } | 39 | } |
| 48 | 40 | ||
| 49 | - .file-mode{ | 41 | + .file-mode { |
| 50 | font-family: $monospace_font; | 42 | font-family: $monospace_font; |
| 51 | } | 43 | } |
| 52 | } | 44 | } |
| @@ -56,13 +48,13 @@ | @@ -56,13 +48,13 @@ | ||
| 56 | background: #FFF; | 48 | background: #FFF; |
| 57 | color: #333; | 49 | color: #333; |
| 58 | font-size: 12px; | 50 | font-size: 12px; |
| 59 | - .old{ | ||
| 60 | - span.idiff{ | 51 | + .old { |
| 52 | + span.idiff { | ||
| 61 | background-color: #FAA; | 53 | background-color: #FAA; |
| 62 | } | 54 | } |
| 63 | } | 55 | } |
| 64 | - .new{ | ||
| 65 | - span.idiff{ | 56 | + .new { |
| 57 | + span.idiff { | ||
| 66 | background-color: #AFA; | 58 | background-color: #AFA; |
| 67 | } | 59 | } |
| 68 | } | 60 | } |
| @@ -78,7 +70,7 @@ | @@ -78,7 +70,7 @@ | ||
| 78 | font-size: 12px; | 70 | font-size: 12px; |
| 79 | } | 71 | } |
| 80 | } | 72 | } |
| 81 | - .old_line, .new_line { | 73 | + .old_line, .new_line, .diff_line { |
| 82 | margin: 0px; | 74 | margin: 0px; |
| 83 | padding: 0px; | 75 | padding: 0px; |
| 84 | border: none; | 76 | border: none; |
| @@ -100,6 +92,15 @@ | @@ -100,6 +92,15 @@ | ||
| 100 | text-decoration: underline; | 92 | text-decoration: underline; |
| 101 | } | 93 | } |
| 102 | } | 94 | } |
| 95 | + &.new { | ||
| 96 | + background: #CFD; | ||
| 97 | + } | ||
| 98 | + &.old { | ||
| 99 | + background: #FDD; | ||
| 100 | + } | ||
| 101 | + } | ||
| 102 | + .diff_line { | ||
| 103 | + padding: 0; | ||
| 103 | } | 104 | } |
| 104 | .line_holder { | 105 | .line_holder { |
| 105 | &.old .old_line, | 106 | &.old .old_line, |
| @@ -130,6 +131,11 @@ | @@ -130,6 +131,11 @@ | ||
| 130 | color: #ccc; | 131 | color: #ccc; |
| 131 | background: #fafafa; | 132 | background: #fafafa; |
| 132 | } | 133 | } |
| 134 | + &.parallel { | ||
| 135 | + display: table-cell; | ||
| 136 | + overflow: hidden; | ||
| 137 | + width: 50%; | ||
| 138 | + } | ||
| 133 | } | 139 | } |
| 134 | } | 140 | } |
| 135 | .image { | 141 | .image { |
app/assets/stylesheets/sections/dashboard.scss
| @@ -51,7 +51,7 @@ | @@ -51,7 +51,7 @@ | ||
| 51 | li { | 51 | li { |
| 52 | &.active { | 52 | &.active { |
| 53 | a { | 53 | a { |
| 54 | - @include linear-gradient(#f5f5f5, #eee); | 54 | + background-color: #EEE; |
| 55 | border-bottom: 1px solid #EEE !important; | 55 | border-bottom: 1px solid #EEE !important; |
| 56 | &:hover { | 56 | &:hover { |
| 57 | background: #eee; | 57 | background: #eee; |
| @@ -100,3 +100,21 @@ | @@ -100,3 +100,21 @@ | ||
| 100 | padding: 2px 5px; | 100 | padding: 2px 5px; |
| 101 | } | 101 | } |
| 102 | } | 102 | } |
| 103 | + | ||
| 104 | +.project-access-icon { | ||
| 105 | + margin-left: 10px; | ||
| 106 | + float: left; | ||
| 107 | + margin-right: 15px; | ||
| 108 | + font-size: 20px; | ||
| 109 | + margin-bottom: 15px; | ||
| 110 | + border: 1px solid #EEE; | ||
| 111 | + padding: 8px 12px; | ||
| 112 | + border-radius: 50px; | ||
| 113 | + background: #f5f5f5; | ||
| 114 | + width: 16px; | ||
| 115 | + text-align: center; | ||
| 116 | + | ||
| 117 | + i { | ||
| 118 | + color: #BBB; | ||
| 119 | + } | ||
| 120 | +} |
app/assets/stylesheets/sections/header.scss
| @@ -36,8 +36,8 @@ header { | @@ -36,8 +36,8 @@ header { | ||
| 36 | float: left; | 36 | float: left; |
| 37 | margin-right: 9px; | 37 | margin-right: 9px; |
| 38 | position: relative; | 38 | position: relative; |
| 39 | - top: -5px; | ||
| 40 | - padding-top: 5px; | 39 | + top: -3px; |
| 40 | + padding-top: 3px; | ||
| 41 | 41 | ||
| 42 | a { | 42 | a { |
| 43 | float: left; | 43 | float: left; |
| @@ -46,8 +46,8 @@ header { | @@ -46,8 +46,8 @@ header { | ||
| 46 | 46 | ||
| 47 | h1 { | 47 | h1 { |
| 48 | margin: 0; | 48 | margin: 0; |
| 49 | - background: url('logo-black.png') no-repeat center 1px; | ||
| 50 | - background-size: 38px; | 49 | + background: url('logo-black.png') no-repeat center center; |
| 50 | + background-size: 32px; | ||
| 51 | float: left; | 51 | float: left; |
| 52 | height: 40px; | 52 | height: 40px; |
| 53 | width: 40px; | 53 | width: 40px; |
| @@ -152,8 +152,8 @@ header { | @@ -152,8 +152,8 @@ header { | ||
| 152 | .app_logo { | 152 | .app_logo { |
| 153 | a { | 153 | a { |
| 154 | h1 { | 154 | h1 { |
| 155 | - background: url('logo-white.png') no-repeat center 1px; | ||
| 156 | - background-size: 38px; | 155 | + background: url('logo-white.png') no-repeat center center; |
| 156 | + background-size: 32px; | ||
| 157 | color: #fff; | 157 | color: #fff; |
| 158 | text-shadow: 0 1px 1px #444; | 158 | text-shadow: 0 1px 1px #444; |
| 159 | } | 159 | } |
app/assets/stylesheets/sections/issues.scss
| @@ -77,8 +77,8 @@ input.check_all_issues { | @@ -77,8 +77,8 @@ input.check_all_issues { | ||
| 77 | @media (min-width: 800px) { .issues_filters select { width: 160px; } } | 77 | @media (min-width: 800px) { .issues_filters select { width: 160px; } } |
| 78 | @media (min-width: 1200px) { .issues_filters select { width: 220px; } } | 78 | @media (min-width: 1200px) { .issues_filters select { width: 220px; } } |
| 79 | 79 | ||
| 80 | -@media (min-width: 800px) { .issues_bulk_update select { width: 120px; } } | ||
| 81 | -@media (min-width: 1200px) { .issues_bulk_update select { width: 160px; } } | 80 | +@media (min-width: 800px) { .issues_bulk_update .chosen-container { min-width: 120px; } } |
| 81 | +@media (min-width: 1200px) { .issues_bulk_update .chosen-container { min-width: 160px; } } | ||
| 82 | 82 | ||
| 83 | .issues-holder { | 83 | .issues-holder { |
| 84 | .issues_filters { | 84 | .issues_filters { |
| @@ -103,3 +103,19 @@ input.check_all_issues { | @@ -103,3 +103,19 @@ input.check_all_issues { | ||
| 103 | .participants { | 103 | .participants { |
| 104 | margin-bottom: 10px; | 104 | margin-bottom: 10px; |
| 105 | } | 105 | } |
| 106 | + | ||
| 107 | +.issues_bulk_update { | ||
| 108 | + .chosen-container { | ||
| 109 | + text-shadow: none; | ||
| 110 | + } | ||
| 111 | +} | ||
| 112 | + | ||
| 113 | +.issue-search-form { | ||
| 114 | + margin: 0; | ||
| 115 | + height: 24px; | ||
| 116 | + | ||
| 117 | + .issue_search { | ||
| 118 | + border: 1px solid #DDD !important; | ||
| 119 | + background-color: #f4f4f4; | ||
| 120 | + } | ||
| 121 | +} |
app/assets/stylesheets/sections/merge_requests.scss
| @@ -110,9 +110,29 @@ | @@ -110,9 +110,29 @@ | ||
| 110 | 110 | ||
| 111 | .merge-request-angle { | 111 | .merge-request-angle { |
| 112 | text-align: center; | 112 | text-align: center; |
| 113 | - margin: 0; | 113 | + margin: 0 auto; |
| 114 | + background: #eee; | ||
| 115 | + border-radius: 100px; | ||
| 116 | + width: 60px; | ||
| 117 | + line-height: 60px; | ||
| 118 | + color: #777; | ||
| 119 | + text-shadow: 0 1px 2px #FFF; | ||
| 114 | } | 120 | } |
| 115 | 121 | ||
| 116 | .merge-request-form-info { | 122 | .merge-request-form-info { |
| 117 | - padding: 15px 0; | 123 | + padding-top: 15px; |
| 124 | +} | ||
| 125 | + | ||
| 126 | +.merge-request-branches { | ||
| 127 | + .commit-row-message { | ||
| 128 | + font-weight: normal !important; | ||
| 129 | + } | ||
| 130 | + | ||
| 131 | + .chosen-container .chosen-single { | ||
| 132 | + padding: 2px 0 2px 10px; | ||
| 133 | + span { | ||
| 134 | + font-weight: bold; | ||
| 135 | + color: #555; | ||
| 136 | + } | ||
| 137 | + } | ||
| 118 | } | 138 | } |
app/assets/stylesheets/sections/notes.scss
| @@ -130,6 +130,12 @@ ul.notes { | @@ -130,6 +130,12 @@ ul.notes { | ||
| 130 | &.notes_line { | 130 | &.notes_line { |
| 131 | text-align: center; | 131 | text-align: center; |
| 132 | padding: 10px 0; | 132 | padding: 10px 0; |
| 133 | + background: #eee; | ||
| 134 | + } | ||
| 135 | + &.notes_line2 { | ||
| 136 | + text-align: center; | ||
| 137 | + padding: 10px 0; | ||
| 138 | + border-left: 1px solid #ddd !important; | ||
| 133 | } | 139 | } |
| 134 | &.notes_content { | 140 | &.notes_content { |
| 135 | background-color: $white; | 141 | background-color: $white; |
| @@ -270,10 +276,9 @@ ul.notes { | @@ -270,10 +276,9 @@ ul.notes { | ||
| 270 | 276 | ||
| 271 | // preview/edit buttons | 277 | // preview/edit buttons |
| 272 | > a { | 278 | > a { |
| 273 | - font-size: 24px; | ||
| 274 | - padding: 4px; | ||
| 275 | position: absolute; | 279 | position: absolute; |
| 276 | - right: 10px; | 280 | + right: 5px; |
| 281 | + bottom: -60px; | ||
| 277 | } | 282 | } |
| 278 | .note_preview { | 283 | .note_preview { |
| 279 | background: #f5f5f5; | 284 | background: #f5f5f5; |
| @@ -306,10 +311,8 @@ ul.notes { | @@ -306,10 +311,8 @@ ul.notes { | ||
| 306 | 311 | ||
| 307 | .common-note-form { | 312 | .common-note-form { |
| 308 | margin: 0; | 313 | margin: 0; |
| 309 | - height: 140px; | ||
| 310 | background: #F9F9F9; | 314 | background: #F9F9F9; |
| 311 | padding: 3px; | 315 | padding: 3px; |
| 312 | - padding-bottom: 25px; | ||
| 313 | border: 1px solid #DDD; | 316 | border: 1px solid #DDD; |
| 314 | } | 317 | } |
| 315 | 318 | ||
| @@ -320,7 +323,7 @@ ul.notes { | @@ -320,7 +323,7 @@ ul.notes { | ||
| 320 | padding: 0 5px; | 323 | padding: 0 5px; |
| 321 | 324 | ||
| 322 | .note-form-option { | 325 | .note-form-option { |
| 323 | - margin-top: 10px; | 326 | + margin-top: 8px; |
| 324 | margin-left: 30px; | 327 | margin-left: 30px; |
| 325 | @extend .pull-left; | 328 | @extend .pull-left; |
| 326 | } | 329 | } |
| @@ -358,3 +361,7 @@ ul.notes { | @@ -358,3 +361,7 @@ ul.notes { | ||
| 358 | .js-note-attachment-delete { | 361 | .js-note-attachment-delete { |
| 359 | display: none; | 362 | display: none; |
| 360 | } | 363 | } |
| 364 | + | ||
| 365 | +.parallel-comment { | ||
| 366 | + padding: 6px; | ||
| 367 | +} |
app/assets/stylesheets/sections/profile.scss
app/assets/stylesheets/sections/projects.scss
| @@ -16,9 +16,15 @@ | @@ -16,9 +16,15 @@ | ||
| 16 | 16 | ||
| 17 | .project-home-panel { | 17 | .project-home-panel { |
| 18 | border-bottom: 1px solid #DDD; | 18 | border-bottom: 1px solid #DDD; |
| 19 | - padding-bottom: 30px; | 19 | + padding-bottom: 25px; |
| 20 | margin-bottom: 30px; | 20 | margin-bottom: 30px; |
| 21 | 21 | ||
| 22 | + &.empty-project { | ||
| 23 | + border-bottom: 0px; | ||
| 24 | + padding-bottom: 15px; | ||
| 25 | + margin-bottom: 0px; | ||
| 26 | + } | ||
| 27 | + | ||
| 22 | .project-home-title { | 28 | .project-home-title { |
| 23 | font-size: 18px; | 29 | font-size: 18px; |
| 24 | color: #777; | 30 | color: #777; |
| @@ -45,7 +51,7 @@ | @@ -45,7 +51,7 @@ | ||
| 45 | } | 51 | } |
| 46 | } | 52 | } |
| 47 | 53 | ||
| 48 | - .public-label { | 54 | + .visibility-level-label { |
| 49 | font-size: 14px; | 55 | font-size: 14px; |
| 50 | background: #f1f1f1; | 56 | background: #f1f1f1; |
| 51 | padding: 8px 10px; | 57 | padding: 8px 10px; |
| @@ -53,6 +59,10 @@ | @@ -53,6 +59,10 @@ | ||
| 53 | margin-left: 10px; | 59 | margin-left: 10px; |
| 54 | color: #888; | 60 | color: #888; |
| 55 | text-shadow: 0 1px 1px #FFF; | 61 | text-shadow: 0 1px 1px #FFF; |
| 62 | + | ||
| 63 | + i { | ||
| 64 | + color: inherit; | ||
| 65 | + } | ||
| 56 | } | 66 | } |
| 57 | } | 67 | } |
| 58 | 68 | ||
| @@ -87,9 +97,40 @@ | @@ -87,9 +97,40 @@ | ||
| 87 | } | 97 | } |
| 88 | } | 98 | } |
| 89 | 99 | ||
| 90 | -.project-public-holder { | ||
| 91 | - .help-inline { | ||
| 92 | - padding-top: 7px; | 100 | +.project-visibility-level-holder { |
| 101 | + .controls { | ||
| 102 | + padding-bottom: 9px; | ||
| 103 | + } | ||
| 104 | + | ||
| 105 | + .controls { | ||
| 106 | + input { | ||
| 107 | + float: left; | ||
| 108 | + } | ||
| 109 | + .descr { | ||
| 110 | + display: block; | ||
| 111 | + margin-left: 1.5em; | ||
| 112 | + &.restricted { | ||
| 113 | + color: #888; | ||
| 114 | + } | ||
| 115 | + | ||
| 116 | + label { | ||
| 117 | + float: none; | ||
| 118 | + padding: 0; | ||
| 119 | + margin: 0; | ||
| 120 | + text-align: left; | ||
| 121 | + } | ||
| 122 | + } | ||
| 123 | + .info { | ||
| 124 | + display: block; | ||
| 125 | + margin-top: 5px; | ||
| 126 | + } | ||
| 127 | + strong { | ||
| 128 | + display: inline-block; | ||
| 129 | + width: 4em; | ||
| 130 | + } | ||
| 131 | + } | ||
| 132 | + i { | ||
| 133 | + color: inherit; | ||
| 93 | } | 134 | } |
| 94 | } | 135 | } |
| 95 | 136 | ||
| @@ -130,7 +171,8 @@ ul.nav.nav-projects-tabs { | @@ -130,7 +171,8 @@ ul.nav.nav-projects-tabs { | ||
| 130 | margin: 0px; | 171 | margin: 0px; |
| 131 | } | 172 | } |
| 132 | 173 | ||
| 133 | -.my-projects { | 174 | +.my-projects, |
| 175 | +.public-projects { | ||
| 134 | li { | 176 | li { |
| 135 | .project-info { | 177 | .project-info { |
| 136 | margin-bottom: 10px; | 178 | margin-bottom: 10px; |
| @@ -166,3 +208,61 @@ ul.nav.nav-projects-tabs { | @@ -166,3 +208,61 @@ ul.nav.nav-projects-tabs { | ||
| 166 | color: #777; | 208 | color: #777; |
| 167 | } | 209 | } |
| 168 | } | 210 | } |
| 211 | + | ||
| 212 | +.project-side { | ||
| 213 | + .btn-block { | ||
| 214 | + background-image: none; | ||
| 215 | + background-color: #F1f1f1; | ||
| 216 | + border-color: #EEE; | ||
| 217 | + &:hover { | ||
| 218 | + background-color: #eee; | ||
| 219 | + border-color: #DDD; | ||
| 220 | + } | ||
| 221 | + } | ||
| 222 | + .project-fork-icon { | ||
| 223 | + float: left; | ||
| 224 | + font-size: 26px; | ||
| 225 | + margin-right: 10px; | ||
| 226 | + line-height: 1.5; | ||
| 227 | + } | ||
| 228 | +} | ||
| 229 | + | ||
| 230 | +.transfer-project .chosen-container { | ||
| 231 | + min-width: 200px; | ||
| 232 | +} | ||
| 233 | + | ||
| 234 | +/** Branch/tag selector **/ | ||
| 235 | +.project-refs-form { | ||
| 236 | + margin: 0; | ||
| 237 | + span { | ||
| 238 | + background:none !important; | ||
| 239 | + position:static !important; | ||
| 240 | + width:auto !important; | ||
| 241 | + height:auto !important; | ||
| 242 | + } | ||
| 243 | +} | ||
| 244 | +.project-refs-select { | ||
| 245 | + width: 120px; | ||
| 246 | +} | ||
| 247 | + | ||
| 248 | +.project-refs-form .chosen-container { | ||
| 249 | + position: relative; | ||
| 250 | + top: 0; | ||
| 251 | + left: 0; | ||
| 252 | + margin-right: 10px; | ||
| 253 | + | ||
| 254 | + .chosen-single span { | ||
| 255 | + font-weight: bold; | ||
| 256 | + color: #555; | ||
| 257 | + } | ||
| 258 | + | ||
| 259 | + &.chosen-container-active { | ||
| 260 | + .chosen-drop { | ||
| 261 | + min-width: 400px; | ||
| 262 | + } | ||
| 263 | + | ||
| 264 | + .chosen-results { | ||
| 265 | + max-height: 400px; | ||
| 266 | + } | ||
| 267 | + } | ||
| 268 | +} |
app/assets/stylesheets/selects.scss
| 1 | -/* CHZN reset few styles */ | ||
| 2 | -.chosen-container-single .chosen-single { | ||
| 3 | - background: #FFF; | ||
| 4 | - border: 1px solid #bbb; | ||
| 5 | - box-shadow: none; | ||
| 6 | -} | ||
| 7 | -.chosen-container-active .chosen-single { | ||
| 8 | - background: #fff; | ||
| 9 | -} | ||
| 10 | - | ||
| 11 | -.ajax-users-select { | ||
| 12 | - width: 400px; | ||
| 13 | - | ||
| 14 | - &.input-large { | ||
| 15 | - width: 210px; | ||
| 16 | - } | ||
| 17 | -} | ||
| 18 | - | ||
| 19 | -.user-result { | ||
| 20 | - .user-image { | ||
| 21 | - float: left; | ||
| 22 | - } | ||
| 23 | - .user-name { | ||
| 24 | - } | ||
| 25 | - .user-username { | ||
| 26 | - color: #999; | ||
| 27 | - } | ||
| 28 | -} | ||
| 29 | - | ||
| 30 | -/** Branch/tag selector **/ | ||
| 31 | -.project-refs-form { | ||
| 32 | - margin: 0; | ||
| 33 | - span { | ||
| 34 | - background:none !important; | ||
| 35 | - position:static !important; | ||
| 36 | - width:auto !important; | ||
| 37 | - height:auto !important; | ||
| 38 | - } | ||
| 39 | -} | ||
| 40 | -.project-refs-select { | ||
| 41 | - width: 120px; | ||
| 42 | -} | ||
| 43 | - | ||
| 44 | -.project-refs-form .chosen-container { | ||
| 45 | - position: relative; | ||
| 46 | - top: 0; | ||
| 47 | - left: 0; | ||
| 48 | - margin-right: 10px; | ||
| 49 | - | ||
| 50 | - .chosen-drop { | ||
| 51 | - min-width: 400px; | ||
| 52 | - .chosen-results { | ||
| 53 | - max-height: 300px; | ||
| 54 | - } | ||
| 55 | - .chosen-search input { | ||
| 56 | - min-width: 365px; | ||
| 57 | - } | ||
| 58 | - } | ||
| 59 | -} | ||
| 60 | - | ||
| 61 | -/** Fix for Search Dropdown Border **/ | 1 | +/** Chosen.js selectbox style override **/ |
| 62 | .chosen-container { | 2 | .chosen-container { |
| 63 | min-width: 100px; | 3 | min-width: 100px; |
| 64 | 4 | ||
| 65 | - .chosen-search { | ||
| 66 | - input:focus { | ||
| 67 | - @include box-shadow(none); | ||
| 68 | - } | 5 | + .chosen-single { |
| 6 | + background: #EEE !important; | ||
| 7 | + border: 1px solid #DDD !important; | ||
| 8 | + @include box-shadow(none !important); | ||
| 9 | + @include border-radius(4px !important); | ||
| 69 | } | 10 | } |
| 70 | 11 | ||
| 71 | - .chosen-drop { | ||
| 72 | - margin: 7px 0; | ||
| 73 | - min-width: 200px; | ||
| 74 | - border: 1px solid #bbb; | ||
| 75 | - @include border-radius(0); | ||
| 76 | - | ||
| 77 | - .chosen-results { | ||
| 78 | - margin-top: 5px; | ||
| 79 | - max-height: 300px; | ||
| 80 | - | ||
| 81 | - .group-result { | ||
| 82 | - color: $style_color; | ||
| 83 | - border-bottom: 1px solid #EEE; | ||
| 84 | - padding: 8px; | ||
| 85 | - } | ||
| 86 | - .active-result { | ||
| 87 | - @include border-radius(0); | ||
| 88 | - | ||
| 89 | - &.highlighted { | ||
| 90 | - background: $hover; | ||
| 91 | - color: $style_color; | ||
| 92 | - } | ||
| 93 | - &.result-selected { | ||
| 94 | - background: #EEE; | ||
| 95 | - border-left: 4px solid #CCC; | ||
| 96 | - } | ||
| 97 | - } | ||
| 98 | - } | ||
| 99 | - | ||
| 100 | - .chosen-search { | ||
| 101 | - @include bg-gray-gradient; | ||
| 102 | - input { | ||
| 103 | - min-width: 165px; | ||
| 104 | - border-color: #CCC; | ||
| 105 | - } | ||
| 106 | - } | 12 | + .chosen-results li.highlighted { |
| 13 | + background: #29b; | ||
| 107 | } | 14 | } |
| 108 | -} | ||
| 109 | 15 | ||
| 110 | -.chosen-container .chosen-single, | ||
| 111 | -.chosen-container.chosen-with-drop .chosen-single { | ||
| 112 | - @include bg-light-gray-gradient; | ||
| 113 | - | ||
| 114 | - div { | ||
| 115 | - background: transparent; | ||
| 116 | - border-left: none; | 16 | + .chosen-drop { |
| 17 | + margin-top: 10px; | ||
| 18 | + border: 1px solid #DDD !important; | ||
| 19 | + @include border-radius(4px !important); | ||
| 117 | } | 20 | } |
| 118 | 21 | ||
| 119 | - span { | ||
| 120 | - font-weight: normal; | 22 | + .chosen-search input { |
| 23 | + border: 1px solid #CCC !important; | ||
| 24 | + @include box-shadow(none !important); | ||
| 121 | } | 25 | } |
| 122 | } | 26 | } |
| 123 | 27 | ||
| 124 | /** Select2 styling **/ | 28 | /** Select2 styling **/ |
| 125 | .select2-container .select2-choice { | 29 | .select2-container .select2-choice { |
| 126 | - background: #f1f1f1; | ||
| 127 | - background-image: -webkit-gradient(linear, 0 0, 0 30, color-stop(0.066, whitesmoke), to(#e1e1e1)); | ||
| 128 | - background-image: -webkit-linear-gradient(whitesmoke 6.6%, #e1e1e1); | ||
| 129 | - background-image: -moz-linear-gradient(whitesmoke 6.6%, #e1e1e1); | ||
| 130 | - background-image: -ms-linear-gradient(whitesmoke 6.6%, #e1e1e1); | ||
| 131 | - background-image: -o-linear-gradient(whitesmoke 6.6%, #e1e1e1); | 30 | + @include bg-light-gray-gradient; |
| 132 | } | 31 | } |
| 133 | 32 | ||
| 134 | .select2-container .select2-choice div { | 33 | .select2-container .select2-choice div { |
app/assets/stylesheets/themes/ui_color.scss
app/assets/stylesheets/themes/ui_mars.scss
| @@ -23,12 +23,17 @@ | @@ -23,12 +23,17 @@ | ||
| 23 | background-color: #373D47; | 23 | background-color: #373D47; |
| 24 | } | 24 | } |
| 25 | } | 25 | } |
| 26 | + .separator { | ||
| 27 | + background: #373D47; | ||
| 28 | + border-left: 1px solid #575D67; | ||
| 29 | + } | ||
| 30 | + .nav > li > a { | ||
| 31 | + color: #979DA7; | ||
| 32 | + } | ||
| 33 | + .search-input { | ||
| 34 | + border-color: #979DA7; | ||
| 35 | + } | ||
| 26 | } | 36 | } |
| 27 | } | 37 | } |
| 28 | - | ||
| 29 | - .separator { | ||
| 30 | - background: #31363E; | ||
| 31 | - border-left: 1px solid #666; | ||
| 32 | - } | ||
| 33 | } | 38 | } |
| 34 | } | 39 | } |
app/assets/stylesheets/themes/ui_modern.scss
app/contexts/files/create_context.rb
| @@ -15,29 +15,23 @@ module Files | @@ -15,29 +15,23 @@ module Files | ||
| 15 | return error("You can only create files if you are on top of a branch") | 15 | return error("You can only create files if you are on top of a branch") |
| 16 | end | 16 | end |
| 17 | 17 | ||
| 18 | - file_name = params[:file_name] | 18 | + file_name = File.basename(path) |
| 19 | + file_path = path | ||
| 19 | 20 | ||
| 20 | unless file_name =~ Gitlab::Regex.path_regex | 21 | unless file_name =~ Gitlab::Regex.path_regex |
| 21 | return error("Your changes could not be commited, because file name contains not allowed characters") | 22 | return error("Your changes could not be commited, because file name contains not allowed characters") |
| 22 | end | 23 | end |
| 23 | 24 | ||
| 24 | - file_path = if path.blank? | ||
| 25 | - file_name | ||
| 26 | - else | ||
| 27 | - File.join(path, file_name) | ||
| 28 | - end | ||
| 29 | - | ||
| 30 | blob = repository.blob_at(ref, file_path) | 25 | blob = repository.blob_at(ref, file_path) |
| 31 | 26 | ||
| 32 | if blob | 27 | if blob |
| 33 | return error("Your changes could not be commited, because file with such name exists") | 28 | return error("Your changes could not be commited, because file with such name exists") |
| 34 | end | 29 | end |
| 35 | 30 | ||
| 36 | - new_file_action = Gitlab::Satellite::NewFileAction.new(current_user, project, ref, path) | 31 | + new_file_action = Gitlab::Satellite::NewFileAction.new(current_user, project, ref, file_path) |
| 37 | created_successfully = new_file_action.commit!( | 32 | created_successfully = new_file_action.commit!( |
| 38 | params[:content], | 33 | params[:content], |
| 39 | - params[:commit_message], | ||
| 40 | - file_name, | 34 | + params[:commit_message] |
| 41 | ) | 35 | ) |
| 42 | 36 | ||
| 43 | if created_successfully | 37 | if created_successfully |
| @@ -0,0 +1,38 @@ | @@ -0,0 +1,38 @@ | ||
| 1 | +module Files | ||
| 2 | + class DeleteContext < BaseContext | ||
| 3 | + def execute | ||
| 4 | + allowed = if project.protected_branch?(ref) | ||
| 5 | + can?(current_user, :push_code_to_protected_branches, project) | ||
| 6 | + else | ||
| 7 | + can?(current_user, :push_code, project) | ||
| 8 | + end | ||
| 9 | + | ||
| 10 | + unless allowed | ||
| 11 | + return error("You are not allowed to push into this branch") | ||
| 12 | + end | ||
| 13 | + | ||
| 14 | + unless repository.branch_names.include?(ref) | ||
| 15 | + return error("You can only create files if you are on top of a branch") | ||
| 16 | + end | ||
| 17 | + | ||
| 18 | + blob = repository.blob_at(ref, path) | ||
| 19 | + | ||
| 20 | + unless blob | ||
| 21 | + return error("You can only edit text files") | ||
| 22 | + end | ||
| 23 | + | ||
| 24 | + delete_file_action = Gitlab::Satellite::DeleteFileAction.new(current_user, project, ref, path) | ||
| 25 | + | ||
| 26 | + deleted_successfully = delete_file_action.commit!( | ||
| 27 | + nil, | ||
| 28 | + params[:commit_message] | ||
| 29 | + ) | ||
| 30 | + | ||
| 31 | + if deleted_successfully | ||
| 32 | + success | ||
| 33 | + else | ||
| 34 | + error("Your changes could not be commited, because the file has been changed") | ||
| 35 | + end | ||
| 36 | + end | ||
| 37 | + end | ||
| 38 | +end |
app/contexts/files/update_context.rb
| @@ -24,8 +24,7 @@ module Files | @@ -24,8 +24,7 @@ module Files | ||
| 24 | new_file_action = Gitlab::Satellite::EditFileAction.new(current_user, project, ref, path) | 24 | new_file_action = Gitlab::Satellite::EditFileAction.new(current_user, project, ref, path) |
| 25 | created_successfully = new_file_action.commit!( | 25 | created_successfully = new_file_action.commit!( |
| 26 | params[:content], | 26 | params[:content], |
| 27 | - params[:commit_message], | ||
| 28 | - params[:last_commit] | 27 | + params[:commit_message] |
| 29 | ) | 28 | ) |
| 30 | 29 | ||
| 31 | if created_successfully | 30 | if created_successfully |
app/contexts/issues/list_context.rb
| @@ -29,8 +29,26 @@ module Issues | @@ -29,8 +29,26 @@ module Issues | ||
| 29 | if params[:milestone_id].present? | 29 | if params[:milestone_id].present? |
| 30 | @issues = @issues.where(milestone_id: (params[:milestone_id] == '0' ? nil : params[:milestone_id])) | 30 | @issues = @issues.where(milestone_id: (params[:milestone_id] == '0' ? nil : params[:milestone_id])) |
| 31 | end | 31 | end |
| 32 | + | ||
| 33 | + # Sort by :sort param | ||
| 34 | + @issues = sort(@issues, params[:sort]) | ||
| 32 | 35 | ||
| 33 | @issues | 36 | @issues |
| 34 | end | 37 | end |
| 38 | + | ||
| 39 | + private | ||
| 40 | + | ||
| 41 | + def sort(issues, condition) | ||
| 42 | + case condition | ||
| 43 | + when 'newest' then issues.except(:order).order('created_at DESC') | ||
| 44 | + when 'oldest' then issues.except(:order).order('created_at ASC') | ||
| 45 | + when 'recently_updated' then issues.except(:order).order('updated_at DESC') | ||
| 46 | + when 'last_updated' then issues.except(:order).order('updated_at ASC') | ||
| 47 | + when 'milestone_due_soon' then issues.except(:order).joins(:milestone).order("milestones.due_date ASC") | ||
| 48 | + when 'milestone_due_later' then issues.except(:order).joins(:milestone).order("milestones.due_date DESC") | ||
| 49 | + else issues | ||
| 50 | + end | ||
| 51 | + end | ||
| 52 | + | ||
| 35 | end | 53 | end |
| 36 | end | 54 | end |
app/contexts/projects/create_context.rb
| @@ -8,6 +8,11 @@ module Projects | @@ -8,6 +8,11 @@ module Projects | ||
| 8 | # get namespace id | 8 | # get namespace id |
| 9 | namespace_id = params.delete(:namespace_id) | 9 | namespace_id = params.delete(:namespace_id) |
| 10 | 10 | ||
| 11 | + # check that user is allowed to set specified visibility_level | ||
| 12 | + unless Gitlab::VisibilityLevel.allowed_for?(current_user, params[:visibility_level]) | ||
| 13 | + params.delete(:visibility_level) | ||
| 14 | + end | ||
| 15 | + | ||
| 11 | # Load default feature settings | 16 | # Load default feature settings |
| 12 | default_features = Gitlab.config.gitlab.default_projects_features | 17 | default_features = Gitlab.config.gitlab.default_projects_features |
| 13 | 18 | ||
| @@ -17,7 +22,7 @@ module Projects | @@ -17,7 +22,7 @@ module Projects | ||
| 17 | wall_enabled: default_features.wall, | 22 | wall_enabled: default_features.wall, |
| 18 | snippets_enabled: default_features.snippets, | 23 | snippets_enabled: default_features.snippets, |
| 19 | merge_requests_enabled: default_features.merge_requests, | 24 | merge_requests_enabled: default_features.merge_requests, |
| 20 | - public: default_features.public | 25 | + visibility_level: default_features.visibility_level |
| 21 | }.stringify_keys | 26 | }.stringify_keys |
| 22 | 27 | ||
| 23 | @project = Project.new(default_opts.merge(params)) | 28 | @project = Project.new(default_opts.merge(params)) |
| @@ -47,8 +52,6 @@ module Projects | @@ -47,8 +52,6 @@ module Projects | ||
| 47 | @project.creator = current_user | 52 | @project.creator = current_user |
| 48 | 53 | ||
| 49 | if @project.save | 54 | if @project.save |
| 50 | - @project.discover_default_branch | ||
| 51 | - | ||
| 52 | unless @project.group | 55 | unless @project.group |
| 53 | @project.users_projects.create( | 56 | @project.users_projects.create( |
| 54 | project_access: UsersProject::MASTER, | 57 | project_access: UsersProject::MASTER, |
app/contexts/projects/update_context.rb
| @@ -2,7 +2,23 @@ module Projects | @@ -2,7 +2,23 @@ module Projects | ||
| 2 | class UpdateContext < BaseContext | 2 | class UpdateContext < BaseContext |
| 3 | def execute(role = :default) | 3 | def execute(role = :default) |
| 4 | params[:project].delete(:namespace_id) | 4 | params[:project].delete(:namespace_id) |
| 5 | - params[:project].delete(:public) unless can?(current_user, :change_public_mode, project) | 5 | + # check that user is allowed to set specified visibility_level |
| 6 | + unless can?(current_user, :change_visibility_level, project) && Gitlab::VisibilityLevel.allowed_for?(current_user, params[:project][:visibility_level]) | ||
| 7 | + params[:project].delete(:visibility_level) | ||
| 8 | + end | ||
| 9 | + | ||
| 10 | + new_branch = params[:project].delete(:default_branch) | ||
| 11 | + | ||
| 12 | + if project.repository.exists? && new_branch != project.default_branch | ||
| 13 | + GitlabShellWorker.perform_async( | ||
| 14 | + :update_repository_head, | ||
| 15 | + project.path_with_namespace, | ||
| 16 | + new_branch | ||
| 17 | + ) | ||
| 18 | + | ||
| 19 | + project.reload_default_branch | ||
| 20 | + end | ||
| 21 | + | ||
| 6 | project.update_attributes(params[:project], as: role) | 22 | project.update_attributes(params[:project], as: role) |
| 7 | end | 23 | end |
| 8 | end | 24 | end |
app/contexts/search_context.rb
| 1 | class SearchContext | 1 | class SearchContext |
| 2 | - attr_accessor :project_ids, :params | 2 | + attr_accessor :project_ids, :current_user, :params |
| 3 | 3 | ||
| 4 | - def initialize(project_ids, params) | ||
| 5 | - @project_ids, @params = project_ids, params.dup | 4 | + def initialize(project_ids, user, params) |
| 5 | + @project_ids, @current_user, @params = project_ids, user, params.dup | ||
| 6 | end | 6 | end |
| 7 | 7 | ||
| 8 | def execute | 8 | def execute |
| @@ -10,7 +10,8 @@ class SearchContext | @@ -10,7 +10,8 @@ class SearchContext | ||
| 10 | query = Shellwords.shellescape(query) if query.present? | 10 | query = Shellwords.shellescape(query) if query.present? |
| 11 | 11 | ||
| 12 | return result unless query.present? | 12 | return result unless query.present? |
| 13 | - result[:projects] = Project.where("projects.id in (?) OR projects.public = true", project_ids).search(query).limit(20) | 13 | + visibility_levels = @current_user ? [ Gitlab::VisibilityLevel::INTERNAL, Gitlab::VisibilityLevel::PUBLIC ] : [ Gitlab::VisibilityLevel::PUBLIC ] |
| 14 | + result[:projects] = Project.where("projects.id in (?) OR projects.visibility_level in (?)", project_ids, visibility_levels).search(query).limit(20) | ||
| 14 | 15 | ||
| 15 | # Search inside single project | 16 | # Search inside single project |
| 16 | single_project_search(Project.where(id: project_ids), query) | 17 | single_project_search(Project.where(id: project_ids), query) |
| @@ -0,0 +1,32 @@ | @@ -0,0 +1,32 @@ | ||
| 1 | +class Admin::BroadcastMessagesController < Admin::ApplicationController | ||
| 2 | + before_filter :broadcast_messages | ||
| 3 | + | ||
| 4 | + def index | ||
| 5 | + @broadcast_message = BroadcastMessage.new | ||
| 6 | + end | ||
| 7 | + | ||
| 8 | + def create | ||
| 9 | + @broadcast_message = BroadcastMessage.new(params[:broadcast_message]) | ||
| 10 | + | ||
| 11 | + if @broadcast_message.save | ||
| 12 | + redirect_to admin_broadcast_messages_path, notice: 'Broadcast Message was successfully created.' | ||
| 13 | + else | ||
| 14 | + render :index | ||
| 15 | + end | ||
| 16 | + end | ||
| 17 | + | ||
| 18 | + def destroy | ||
| 19 | + BroadcastMessage.find(params[:id]).destroy | ||
| 20 | + | ||
| 21 | + respond_to do |format| | ||
| 22 | + format.html { redirect_to :back } | ||
| 23 | + format.js { render nothing: true } | ||
| 24 | + end | ||
| 25 | + end | ||
| 26 | + | ||
| 27 | + protected | ||
| 28 | + | ||
| 29 | + def broadcast_messages | ||
| 30 | + @broadcast_messages ||= BroadcastMessage.order("starts_at DESC").page(params[:page]) | ||
| 31 | + end | ||
| 32 | +end |
app/controllers/admin/dashboard_controller.rb
| @@ -2,5 +2,6 @@ class Admin::DashboardController < Admin::ApplicationController | @@ -2,5 +2,6 @@ class Admin::DashboardController < Admin::ApplicationController | ||
| 2 | def index | 2 | def index |
| 3 | @projects = Project.order("created_at DESC").limit(10) | 3 | @projects = Project.order("created_at DESC").limit(10) |
| 4 | @users = User.order("created_at DESC").limit(10) | 4 | @users = User.order("created_at DESC").limit(10) |
| 5 | + @groups = Group.order("created_at DESC").limit(10) | ||
| 5 | end | 6 | end |
| 6 | end | 7 | end |
app/controllers/admin/projects_controller.rb
| 1 | class Admin::ProjectsController < Admin::ApplicationController | 1 | class Admin::ProjectsController < Admin::ApplicationController |
| 2 | - before_filter :project, only: [:edit, :show, :update, :destroy, :team_update] | 2 | + before_filter :project, only: [:show, :transfer] |
| 3 | + before_filter :group, only: [:show, :transfer] | ||
| 4 | + before_filter :repository, only: [:show, :transfer] | ||
| 3 | 5 | ||
| 4 | def index | 6 | def index |
| 5 | owner_id = params[:owner_id] | 7 | owner_id = params[:owner_id] |
| 6 | user = User.find_by_id(owner_id) | 8 | user = User.find_by_id(owner_id) |
| 7 | 9 | ||
| 8 | @projects = user ? user.owned_projects : Project.scoped | 10 | @projects = user ? user.owned_projects : Project.scoped |
| 9 | - @projects = @projects.where(public: true) if params[:public_only].present? | 11 | + @projects = @projects.where("visibility_level IN (?)", params[:visibility_levels]) if params[:visibility_levels].present? |
| 10 | @projects = @projects.with_push if params[:with_push].present? | 12 | @projects = @projects.with_push if params[:with_push].present? |
| 11 | @projects = @projects.abandoned if params[:abandoned].present? | 13 | @projects = @projects.abandoned if params[:abandoned].present? |
| 12 | @projects = @projects.search(params[:name]) if params[:name].present? | 14 | @projects = @projects.search(params[:name]) if params[:name].present? |
| @@ -14,8 +16,16 @@ class Admin::ProjectsController < Admin::ApplicationController | @@ -14,8 +16,16 @@ class Admin::ProjectsController < Admin::ApplicationController | ||
| 14 | end | 16 | end |
| 15 | 17 | ||
| 16 | def show | 18 | def show |
| 17 | - @repository = @project.repository | ||
| 18 | - @group = @project.group | 19 | + end |
| 20 | + | ||
| 21 | + def transfer | ||
| 22 | + result = ::Projects::TransferContext.new(@project, current_user, project: params).execute(:admin) | ||
| 23 | + | ||
| 24 | + if result | ||
| 25 | + redirect_to [:admin, @project] | ||
| 26 | + else | ||
| 27 | + render :show | ||
| 28 | + end | ||
| 19 | end | 29 | end |
| 20 | 30 | ||
| 21 | protected | 31 | protected |
| @@ -26,4 +36,12 @@ class Admin::ProjectsController < Admin::ApplicationController | @@ -26,4 +36,12 @@ class Admin::ProjectsController < Admin::ApplicationController | ||
| 26 | @project = Project.find_with_namespace(id) | 36 | @project = Project.find_with_namespace(id) |
| 27 | @project || render_404 | 37 | @project || render_404 |
| 28 | end | 38 | end |
| 39 | + | ||
| 40 | + def group | ||
| 41 | + @group ||= project.group | ||
| 42 | + end | ||
| 43 | + | ||
| 44 | + def repository | ||
| 45 | + @repository ||= project.repository | ||
| 46 | + end | ||
| 29 | end | 47 | end |
app/controllers/application_controller.rb
| @@ -81,6 +81,9 @@ class ApplicationController < ActionController::Base | @@ -81,6 +81,9 @@ class ApplicationController < ActionController::Base | ||
| 81 | 81 | ||
| 82 | if @project and can?(current_user, :read_project, @project) | 82 | if @project and can?(current_user, :read_project, @project) |
| 83 | @project | 83 | @project |
| 84 | + elsif current_user.nil? | ||
| 85 | + @project = nil | ||
| 86 | + authenticate_user! | ||
| 84 | else | 87 | else |
| 85 | @project = nil | 88 | @project = nil |
| 86 | render_404 and return | 89 | render_404 and return |
| @@ -102,7 +105,7 @@ class ApplicationController < ActionController::Base | @@ -102,7 +105,7 @@ class ApplicationController < ActionController::Base | ||
| 102 | end | 105 | end |
| 103 | 106 | ||
| 104 | def authorize_code_access! | 107 | def authorize_code_access! |
| 105 | - return access_denied! unless can?(current_user, :download_code, project) or project.public? | 108 | + return access_denied! unless can?(current_user, :download_code, project) |
| 106 | end | 109 | end |
| 107 | 110 | ||
| 108 | def authorize_push! | 111 | def authorize_push! |
| @@ -174,4 +177,26 @@ class ApplicationController < ActionController::Base | @@ -174,4 +177,26 @@ class ApplicationController < ActionController::Base | ||
| 174 | filters = cookies['event_filter'].split(',') if cookies['event_filter'].present? | 177 | filters = cookies['event_filter'].split(',') if cookies['event_filter'].present? |
| 175 | @event_filter ||= EventFilter.new(filters) | 178 | @event_filter ||= EventFilter.new(filters) |
| 176 | end | 179 | end |
| 180 | + | ||
| 181 | + # JSON for infinite scroll via Pager object | ||
| 182 | + def pager_json(partial, count) | ||
| 183 | + html = render_to_string( | ||
| 184 | + partial, | ||
| 185 | + layout: false, | ||
| 186 | + formats: [:html] | ||
| 187 | + ) | ||
| 188 | + | ||
| 189 | + render json: { | ||
| 190 | + html: html, | ||
| 191 | + count: count | ||
| 192 | + } | ||
| 193 | + end | ||
| 194 | + | ||
| 195 | + def view_to_html_string(partial) | ||
| 196 | + render_to_string( | ||
| 197 | + partial, | ||
| 198 | + layout: false, | ||
| 199 | + formats: [:html] | ||
| 200 | + ) | ||
| 201 | + end | ||
| 177 | end | 202 | end |
app/controllers/dashboard_controller.rb
| @@ -22,7 +22,7 @@ class DashboardController < ApplicationController | @@ -22,7 +22,7 @@ class DashboardController < ApplicationController | ||
| 22 | 22 | ||
| 23 | respond_to do |format| | 23 | respond_to do |format| |
| 24 | format.html | 24 | format.html |
| 25 | - format.js | 25 | + format.json { pager_json("events/_events", @events.count) } |
| 26 | format.atom { render layout: false } | 26 | format.atom { render layout: false } |
| 27 | end | 27 | end |
| 28 | end | 28 | end |
| @@ -40,6 +40,7 @@ class DashboardController < ApplicationController | @@ -40,6 +40,7 @@ class DashboardController < ApplicationController | ||
| 40 | end | 40 | end |
| 41 | 41 | ||
| 42 | @projects = @projects.where(namespace_id: Group.find_by_name(params[:group])) if params[:group].present? | 42 | @projects = @projects.where(namespace_id: Group.find_by_name(params[:group])) if params[:group].present? |
| 43 | + @projects = @projects.where(visibility_level: params[:visibility_level]) if params[:visibility_level].present? | ||
| 43 | @projects = @projects.includes(:namespace).sorted_by_activity | 44 | @projects = @projects.includes(:namespace).sorted_by_activity |
| 44 | 45 | ||
| 45 | @labels = current_user.authorized_projects.tags_on(:labels) | 46 | @labels = current_user.authorized_projects.tags_on(:labels) |
app/controllers/groups_controller.rb
| @@ -38,7 +38,7 @@ class GroupsController < ApplicationController | @@ -38,7 +38,7 @@ class GroupsController < ApplicationController | ||
| 38 | 38 | ||
| 39 | respond_to do |format| | 39 | respond_to do |format| |
| 40 | format.html | 40 | format.html |
| 41 | - format.js | 41 | + format.json { pager_json("events/_events", @events.count) } |
| 42 | format.atom { render layout: false } | 42 | format.atom { render layout: false } |
| 43 | end | 43 | end |
| 44 | end | 44 | end |
app/controllers/profiles_controller.rb
| @@ -13,6 +13,8 @@ class ProfilesController < ApplicationController | @@ -13,6 +13,8 @@ class ProfilesController < ApplicationController | ||
| 13 | end | 13 | end |
| 14 | 14 | ||
| 15 | def update | 15 | def update |
| 16 | + params[:user].delete(:email) if @user.ldap_user? | ||
| 17 | + | ||
| 16 | if @user.update_attributes(params[:user]) | 18 | if @user.update_attributes(params[:user]) |
| 17 | flash[:notice] = "Profile was successfully updated" | 19 | flash[:notice] = "Profile was successfully updated" |
| 18 | else | 20 | else |
app/controllers/projects/application_controller.rb
| @@ -10,7 +10,7 @@ class Projects::ApplicationController < ApplicationController | @@ -10,7 +10,7 @@ class Projects::ApplicationController < ApplicationController | ||
| 10 | id = params[:project_id] || params[:id] | 10 | id = params[:project_id] || params[:id] |
| 11 | @project = Project.find_with_namespace(id) | 11 | @project = Project.find_with_namespace(id) |
| 12 | 12 | ||
| 13 | - return if @project && @project.public | 13 | + return if @project && @project.public? |
| 14 | end | 14 | end |
| 15 | 15 | ||
| 16 | super | 16 | super |
app/controllers/projects/blob_controller.rb
| @@ -7,9 +7,30 @@ class Projects::BlobController < Projects::ApplicationController | @@ -7,9 +7,30 @@ class Projects::BlobController < Projects::ApplicationController | ||
| 7 | before_filter :authorize_code_access! | 7 | before_filter :authorize_code_access! |
| 8 | before_filter :require_non_empty_project | 8 | before_filter :require_non_empty_project |
| 9 | 9 | ||
| 10 | + before_filter :blob | ||
| 11 | + | ||
| 10 | def show | 12 | def show |
| 11 | - @blob = @repository.blob_at(@commit.id, @path) | 13 | + end |
| 14 | + | ||
| 15 | + def destroy | ||
| 16 | + result = Files::DeleteContext.new(@project, current_user, params, @ref, @path).execute | ||
| 17 | + | ||
| 18 | + if result[:status] == :success | ||
| 19 | + flash[:notice] = "Your changes have been successfully commited" | ||
| 20 | + redirect_to project_tree_path(@project, @ref) | ||
| 21 | + else | ||
| 22 | + flash[:alert] = result[:error] | ||
| 23 | + render :show | ||
| 24 | + end | ||
| 25 | + end | ||
| 26 | + | ||
| 27 | + private | ||
| 28 | + | ||
| 29 | + def blob | ||
| 30 | + @blob ||= @repository.blob_at(@commit.id, @path) | ||
| 31 | + | ||
| 32 | + return not_found! unless @blob | ||
| 12 | 33 | ||
| 13 | - not_found! unless @blob | 34 | + @blob |
| 14 | end | 35 | end |
| 15 | end | 36 | end |
app/controllers/projects/commits_controller.rb
| @@ -16,7 +16,7 @@ class Projects::CommitsController < Projects::ApplicationController | @@ -16,7 +16,7 @@ class Projects::CommitsController < Projects::ApplicationController | ||
| 16 | 16 | ||
| 17 | respond_to do |format| | 17 | respond_to do |format| |
| 18 | format.html # index.html.erb | 18 | format.html # index.html.erb |
| 19 | - format.js | 19 | + format.json { pager_json("projects/commits/_commits", @commits.size) } |
| 20 | format.atom { render layout: false } | 20 | format.atom { render layout: false } |
| 21 | end | 21 | end |
| 22 | end | 22 | end |
app/controllers/projects/issues_controller.rb
| @@ -11,7 +11,7 @@ class Projects::IssuesController < Projects::ApplicationController | @@ -11,7 +11,7 @@ class Projects::IssuesController < Projects::ApplicationController | ||
| 11 | # Allow modify issue | 11 | # Allow modify issue |
| 12 | before_filter :authorize_modify_issue!, only: [:edit, :update] | 12 | before_filter :authorize_modify_issue!, only: [:edit, :update] |
| 13 | 13 | ||
| 14 | - respond_to :js, :html | 14 | + respond_to :html |
| 15 | 15 | ||
| 16 | def index | 16 | def index |
| 17 | terms = params['issue_search'] | 17 | terms = params['issue_search'] |
| @@ -23,11 +23,18 @@ class Projects::IssuesController < Projects::ApplicationController | @@ -23,11 +23,18 @@ class Projects::IssuesController < Projects::ApplicationController | ||
| 23 | assignee_id, milestone_id = params[:assignee_id], params[:milestone_id] | 23 | assignee_id, milestone_id = params[:assignee_id], params[:milestone_id] |
| 24 | @assignee = @project.team.find(assignee_id) if assignee_id.present? && !assignee_id.to_i.zero? | 24 | @assignee = @project.team.find(assignee_id) if assignee_id.present? && !assignee_id.to_i.zero? |
| 25 | @milestone = @project.milestones.find(milestone_id) if milestone_id.present? && !milestone_id.to_i.zero? | 25 | @milestone = @project.milestones.find(milestone_id) if milestone_id.present? && !milestone_id.to_i.zero? |
| 26 | + sort_param = params[:sort] || 'newest' | ||
| 27 | + @sort = sort_param.humanize unless sort_param.empty? | ||
| 28 | + | ||
| 26 | 29 | ||
| 27 | respond_to do |format| | 30 | respond_to do |format| |
| 28 | - format.html # index.html.erb | ||
| 29 | - format.js | 31 | + format.html |
| 30 | format.atom { render layout: false } | 32 | format.atom { render layout: false } |
| 33 | + format.json do | ||
| 34 | + render json: { | ||
| 35 | + html: view_to_html_string("projects/issues/_issues") | ||
| 36 | + } | ||
| 37 | + end | ||
| 31 | end | 38 | end |
| 32 | end | 39 | end |
| 33 | 40 | ||
| @@ -45,10 +52,7 @@ class Projects::IssuesController < Projects::ApplicationController | @@ -45,10 +52,7 @@ class Projects::IssuesController < Projects::ApplicationController | ||
| 45 | @target_type = :issue | 52 | @target_type = :issue |
| 46 | @target_id = @issue.id | 53 | @target_id = @issue.id |
| 47 | 54 | ||
| 48 | - respond_to do |format| | ||
| 49 | - format.html | ||
| 50 | - format.js | ||
| 51 | - end | 55 | + respond_with(@issue) |
| 52 | end | 56 | end |
| 53 | 57 | ||
| 54 | def create | 58 | def create |
app/controllers/projects/merge_requests_controller.rb
| @@ -2,8 +2,8 @@ require 'gitlab/satellite/satellite' | @@ -2,8 +2,8 @@ require 'gitlab/satellite/satellite' | ||
| 2 | 2 | ||
| 3 | class Projects::MergeRequestsController < Projects::ApplicationController | 3 | class Projects::MergeRequestsController < Projects::ApplicationController |
| 4 | before_filter :module_enabled | 4 | before_filter :module_enabled |
| 5 | - before_filter :merge_request, only: [:edit, :update, :show, :commits, :diffs, :automerge, :automerge_check, :ci_status] | ||
| 6 | - before_filter :closes_issues, only: [:edit, :update, :show, :commits, :diffs] | 5 | + before_filter :merge_request, only: [:edit, :update, :show, :diffs, :automerge, :automerge_check, :ci_status] |
| 6 | + before_filter :closes_issues, only: [:edit, :update, :show, :diffs] | ||
| 7 | before_filter :validates_merge_request, only: [:show, :diffs] | 7 | before_filter :validates_merge_request, only: [:show, :diffs] |
| 8 | before_filter :define_show_vars, only: [:show, :diffs] | 8 | before_filter :define_show_vars, only: [:show, :diffs] |
| 9 | 9 | ||
| @@ -26,8 +26,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController | @@ -26,8 +26,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController | ||
| 26 | def show | 26 | def show |
| 27 | respond_to do |format| | 27 | respond_to do |format| |
| 28 | format.html | 28 | format.html |
| 29 | - format.js | ||
| 30 | - | ||
| 31 | format.diff { render text: @merge_request.to_diff(current_user) } | 29 | format.diff { render text: @merge_request.to_diff(current_user) } |
| 32 | format.patch { render text: @merge_request.to_patch(current_user) } | 30 | format.patch { render text: @merge_request.to_patch(current_user) } |
| 33 | end | 31 | end |
| @@ -44,6 +42,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController | @@ -44,6 +42,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController | ||
| 44 | diff_line_count = Commit::diff_line_count(@merge_request.diffs) | 42 | diff_line_count = Commit::diff_line_count(@merge_request.diffs) |
| 45 | @suppress_diff = Commit::diff_suppress?(@merge_request.diffs, diff_line_count) && !params[:force_show_diff] | 43 | @suppress_diff = Commit::diff_suppress?(@merge_request.diffs, diff_line_count) && !params[:force_show_diff] |
| 46 | @force_suppress_diff = Commit::diff_force_suppress?(@merge_request.diffs, diff_line_count) | 44 | @force_suppress_diff = Commit::diff_force_suppress?(@merge_request.diffs, diff_line_count) |
| 45 | + | ||
| 46 | + respond_to do |format| | ||
| 47 | + format.html | ||
| 48 | + format.json { render json: { html: view_to_html_string("projects/merge_requests/show/_diffs") } } | ||
| 49 | + end | ||
| 47 | end | 50 | end |
| 48 | 51 | ||
| 49 | def new | 52 | def new |
app/controllers/projects/milestones_controller.rb
| @@ -34,11 +34,6 @@ class Projects::MilestonesController < Projects::ApplicationController | @@ -34,11 +34,6 @@ class Projects::MilestonesController < Projects::ApplicationController | ||
| 34 | @issues = @milestone.issues | 34 | @issues = @milestone.issues |
| 35 | @users = @milestone.participants.uniq | 35 | @users = @milestone.participants.uniq |
| 36 | @merge_requests = @milestone.merge_requests | 36 | @merge_requests = @milestone.merge_requests |
| 37 | - | ||
| 38 | - respond_to do |format| | ||
| 39 | - format.html | ||
| 40 | - format.js | ||
| 41 | - end | ||
| 42 | end | 37 | end |
| 43 | 38 | ||
| 44 | def create | 39 | def create |
app/controllers/projects/new_tree_controller.rb
| @@ -5,11 +5,12 @@ class Projects::NewTreeController < Projects::BaseTreeController | @@ -5,11 +5,12 @@ class Projects::NewTreeController < Projects::BaseTreeController | ||
| 5 | end | 5 | end |
| 6 | 6 | ||
| 7 | def update | 7 | def update |
| 8 | - result = Files::CreateContext.new(@project, current_user, params, @ref, @path).execute | 8 | + file_path = File.join(@path, File.basename(params[:file_name])) |
| 9 | + result = Files::CreateContext.new(@project, current_user, params, @ref, file_path).execute | ||
| 9 | 10 | ||
| 10 | if result[:status] == :success | 11 | if result[:status] == :success |
| 11 | flash[:notice] = "Your changes have been successfully commited" | 12 | flash[:notice] = "Your changes have been successfully commited" |
| 12 | - redirect_to project_blob_path(@project, File.join(@id, params[:file_name])) | 13 | + redirect_to project_blob_path(@project, File.join(@ref, file_path)) |
| 13 | else | 14 | else |
| 14 | flash[:alert] = result[:error] | 15 | flash[:alert] = result[:error] |
| 15 | render :show | 16 | render :show |
app/controllers/projects/notes_controller.rb
| @@ -14,7 +14,14 @@ class Projects::NotesController < Projects::ApplicationController | @@ -14,7 +14,14 @@ class Projects::NotesController < Projects::ApplicationController | ||
| 14 | @discussions = discussions_from_notes | 14 | @discussions = discussions_from_notes |
| 15 | end | 15 | end |
| 16 | 16 | ||
| 17 | - respond_with(@notes) | 17 | + respond_to do |format| |
| 18 | + format.html { redirect_to :back } | ||
| 19 | + format.json do | ||
| 20 | + render json: { | ||
| 21 | + html: view_to_html_string("projects/notes/_notes") | ||
| 22 | + } | ||
| 23 | + end | ||
| 24 | + end | ||
| 18 | end | 25 | end |
| 19 | 26 | ||
| 20 | def create | 27 | def create |
app/controllers/projects_controller.rb
| @@ -55,17 +55,13 @@ class ProjectsController < ApplicationController | @@ -55,17 +55,13 @@ class ProjectsController < ApplicationController | ||
| 55 | end | 55 | end |
| 56 | 56 | ||
| 57 | def show | 57 | def show |
| 58 | - return authenticate_user! unless @project.public || current_user | 58 | + return authenticate_user! unless @project.public? || current_user |
| 59 | 59 | ||
| 60 | limit = (params[:limit] || 20).to_i | 60 | limit = (params[:limit] || 20).to_i |
| 61 | @events = @project.events.recent | 61 | @events = @project.events.recent |
| 62 | @events = event_filter.apply_filter(@events) | 62 | @events = event_filter.apply_filter(@events) |
| 63 | @events = @events.limit(limit).offset(params[:offset] || 0) | 63 | @events = @events.limit(limit).offset(params[:offset] || 0) |
| 64 | 64 | ||
| 65 | - # Ensure project default branch is set if it possible | ||
| 66 | - # Normally it defined on push or during creation | ||
| 67 | - @project.discover_default_branch | ||
| 68 | - | ||
| 69 | respond_to do |format| | 65 | respond_to do |format| |
| 70 | format.html do | 66 | format.html do |
| 71 | if @project.empty_repo? | 67 | if @project.empty_repo? |
| @@ -77,7 +73,7 @@ class ProjectsController < ApplicationController | @@ -77,7 +73,7 @@ class ProjectsController < ApplicationController | ||
| 77 | render :show, layout: user_layout | 73 | render :show, layout: user_layout |
| 78 | end | 74 | end |
| 79 | end | 75 | end |
| 80 | - format.js | 76 | + format.json { pager_json("events/_events", @events.count) } |
| 81 | end | 77 | end |
| 82 | end | 78 | end |
| 83 | 79 |
app/controllers/public/projects_controller.rb
| @@ -6,7 +6,7 @@ class Public::ProjectsController < ApplicationController | @@ -6,7 +6,7 @@ class Public::ProjectsController < ApplicationController | ||
| 6 | layout 'public' | 6 | layout 'public' |
| 7 | 7 | ||
| 8 | def index | 8 | def index |
| 9 | - @projects = Project.public_only | 9 | + @projects = Project.public_or_internal_only(current_user) |
| 10 | @projects = @projects.search(params[:search]) if params[:search].present? | 10 | @projects = @projects.search(params[:search]) if params[:search].present? |
| 11 | @projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]).per(20) | 11 | @projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]).per(20) |
| 12 | end | 12 | end |
app/controllers/search_controller.rb
| @@ -14,7 +14,7 @@ class SearchController < ApplicationController | @@ -14,7 +14,7 @@ class SearchController < ApplicationController | ||
| 14 | project_ids.select! { |id| id == project_id.to_i} | 14 | project_ids.select! { |id| id == project_id.to_i} |
| 15 | end | 15 | end |
| 16 | 16 | ||
| 17 | - result = SearchContext.new(project_ids, params).execute | 17 | + result = SearchContext.new(project_ids, current_user, params).execute |
| 18 | 18 | ||
| 19 | @projects = result[:projects] | 19 | @projects = result[:projects] |
| 20 | @merge_requests = result[:merge_requests] | 20 | @merge_requests = result[:merge_requests] |
app/helpers/application_helper.rb
| @@ -84,8 +84,8 @@ module ApplicationHelper | @@ -84,8 +84,8 @@ module ApplicationHelper | ||
| 84 | repository = @project.repository | 84 | repository = @project.repository |
| 85 | 85 | ||
| 86 | options = [ | 86 | options = [ |
| 87 | - ["Branch", repository.branch_names ], | ||
| 88 | - [ "Tag", repository.tag_names ] | 87 | + ["Branches", repository.branch_names], |
| 88 | + ["Tags", repository.tag_names] | ||
| 89 | ] | 89 | ] |
| 90 | 90 | ||
| 91 | # If reference is commit id - | 91 | # If reference is commit id - |
| @@ -126,6 +126,9 @@ module ApplicationHelper | @@ -126,6 +126,9 @@ module ApplicationHelper | ||
| 126 | # Skip if user already created appropriate MR | 126 | # Skip if user already created appropriate MR |
| 127 | return false if project.merge_requests.where(source_branch: event.branch_name).opened.any? | 127 | return false if project.merge_requests.where(source_branch: event.branch_name).opened.any? |
| 128 | 128 | ||
| 129 | + # Skip if user removed branch right after that | ||
| 130 | + return false unless project.repository.branch_names.include?(event.branch_name) | ||
| 131 | + | ||
| 129 | true | 132 | true |
| 130 | end | 133 | end |
| 131 | 134 | ||
| @@ -184,14 +187,6 @@ module ApplicationHelper | @@ -184,14 +187,6 @@ module ApplicationHelper | ||
| 184 | Gitlab.config.extra | 187 | Gitlab.config.extra |
| 185 | end | 188 | end |
| 186 | 189 | ||
| 187 | - def public_icon | ||
| 188 | - content_tag :i, nil, class: 'icon-globe cblue' | ||
| 189 | - end | ||
| 190 | - | ||
| 191 | - def private_icon | ||
| 192 | - content_tag :i, nil, class: 'icon-lock cgreen' | ||
| 193 | - end | ||
| 194 | - | ||
| 195 | def search_placeholder | 190 | def search_placeholder |
| 196 | if @project && @project.persisted? | 191 | if @project && @project.persisted? |
| 197 | "Search in this project" | 192 | "Search in this project" |
| @@ -208,4 +203,16 @@ module ApplicationHelper | @@ -208,4 +203,16 @@ module ApplicationHelper | ||
| 208 | line += "..." if lines.size > 1 | 203 | line += "..." if lines.size > 1 |
| 209 | line | 204 | line |
| 210 | end | 205 | end |
| 206 | + | ||
| 207 | + def broadcast_message | ||
| 208 | + BroadcastMessage.current | ||
| 209 | + end | ||
| 210 | + | ||
| 211 | + def highlight_js(&block) | ||
| 212 | + string = capture(&block) | ||
| 213 | + | ||
| 214 | + content_tag :div, class: user_color_scheme_class do | ||
| 215 | + Pygments::Lexer[:js].highlight(string).html_safe | ||
| 216 | + end | ||
| 217 | + end | ||
| 211 | end | 218 | end |
app/helpers/commits_helper.rb
| @@ -105,6 +105,10 @@ module CommitsHelper | @@ -105,6 +105,10 @@ module CommitsHelper | ||
| 105 | branches.sort.map { |branch| link_to(branch, project_tree_path(project, branch)) }.join(", ").html_safe | 105 | branches.sort.map { |branch| link_to(branch, project_tree_path(project, branch)) }.join(", ").html_safe |
| 106 | end | 106 | end |
| 107 | 107 | ||
| 108 | + def get_old_file(project, commit, diff) | ||
| 109 | + project.repository.blob_at(commit.parent_id, diff.old_path) if commit.parent_id | ||
| 110 | + end | ||
| 111 | + | ||
| 108 | protected | 112 | protected |
| 109 | 113 | ||
| 110 | # Private: Returns a link to a person. If the person has a matching user and | 114 | # Private: Returns a link to a person. If the person has a matching user and |
| @@ -125,7 +129,9 @@ module CommitsHelper | @@ -125,7 +129,9 @@ module CommitsHelper | ||
| 125 | source_name | 129 | source_name |
| 126 | end | 130 | end |
| 127 | 131 | ||
| 128 | - user = User.where('name like ? or email like ?', source_name, source_email).first | 132 | + # Prefer email match over name match |
| 133 | + user = User.where(email: source_email).first | ||
| 134 | + user ||= User.where(name: source_name).first | ||
| 129 | 135 | ||
| 130 | options = { | 136 | options = { |
| 131 | class: "commit-#{options[:source]}-link has_tooltip", | 137 | class: "commit-#{options[:source]}-link has_tooltip", |
app/helpers/compare_helper.rb
| 1 | module CompareHelper | 1 | module CompareHelper |
| 2 | def compare_to_mr_button? | 2 | def compare_to_mr_button? |
| 3 | - params[:from].present? && params[:to].present? && | 3 | + @project.merge_requests_enabled && |
| 4 | + params[:from].present? && | ||
| 5 | + params[:to].present? && | ||
| 4 | @repository.branch_names.include?(params[:from]) && | 6 | @repository.branch_names.include?(params[:from]) && |
| 5 | @repository.branch_names.include?(params[:to]) && | 7 | @repository.branch_names.include?(params[:to]) && |
| 6 | params[:from] != params[:to] && | 8 | params[:from] != params[:to] && |
app/helpers/events_helper.rb
| @@ -102,15 +102,11 @@ module EventsHelper | @@ -102,15 +102,11 @@ module EventsHelper | ||
| 102 | end | 102 | end |
| 103 | elsif event.note_project_snippet? | 103 | elsif event.note_project_snippet? |
| 104 | link_to(project_snippet_path(event.project, event.note_target)) do | 104 | link_to(project_snippet_path(event.project, event.note_target)) do |
| 105 | - content_tag :strong do | ||
| 106 | - "#{event.note_target_type} ##{truncate event.note_target_id}" | ||
| 107 | - end | 105 | + "#{event.note_target_type} ##{truncate event.note_target_id}" |
| 108 | end | 106 | end |
| 109 | else | 107 | else |
| 110 | link_to event_note_target_path(event) do | 108 | link_to event_note_target_path(event) do |
| 111 | - content_tag :strong do | ||
| 112 | - "#{event.note_target_type} ##{truncate event.note_target_iid}" | ||
| 113 | - end | 109 | + "#{event.note_target_type} ##{truncate event.note_target_iid}" |
| 114 | end | 110 | end |
| 115 | end | 111 | end |
| 116 | elsif event.wall_note? | 112 | elsif event.wall_note? |
app/helpers/gitlab_markdown_helper.rb
| @@ -64,7 +64,9 @@ module GitlabMarkdownHelper | @@ -64,7 +64,9 @@ module GitlabMarkdownHelper | ||
| 64 | # ref - name of the branch or reference, eg. stable | 64 | # ref - name of the branch or reference, eg. stable |
| 65 | # requested_path - path of request, eg. doc/api/README.md, used in special case when path is pointing to the .md file were the original request is coming from | 65 | # requested_path - path of request, eg. doc/api/README.md, used in special case when path is pointing to the .md file were the original request is coming from |
| 66 | # wiki - whether the markdown is from wiki or not | 66 | # wiki - whether the markdown is from wiki or not |
| 67 | - def create_relative_links(text, project_path_with_namespace, ref, requested_path, wiki = false) | 67 | + def create_relative_links(text, project, ref, requested_path, wiki = false) |
| 68 | + @path_to_satellite = project.satellite.path | ||
| 69 | + project_path_with_namespace = project.path_with_namespace | ||
| 68 | paths = extract_paths(text) | 70 | paths = extract_paths(text) |
| 69 | paths.each do |file_path| | 71 | paths.each do |file_path| |
| 70 | new_path = rebuild_path(project_path_with_namespace, file_path, requested_path, ref) | 72 | new_path = rebuild_path(project_path_with_namespace, file_path, requested_path, ref) |
| @@ -145,13 +147,18 @@ module GitlabMarkdownHelper | @@ -145,13 +147,18 @@ module GitlabMarkdownHelper | ||
| 145 | 147 | ||
| 146 | def file_exists?(path) | 148 | def file_exists?(path) |
| 147 | return false if path.nil? || path.empty? | 149 | return false if path.nil? || path.empty? |
| 148 | - File.exists?(Rails.root.join(path)) | 150 | + File.exists?(path_on_fs(path)) |
| 149 | end | 151 | end |
| 150 | 152 | ||
| 151 | # Check if the path is pointing to a directory(tree) or a file(blob) | 153 | # Check if the path is pointing to a directory(tree) or a file(blob) |
| 152 | # eg. doc/api is directory and doc/README.md is file | 154 | # eg. doc/api is directory and doc/README.md is file |
| 153 | def local_path(path) | 155 | def local_path(path) |
| 154 | - File.directory?(Rails.root.join(path)) ? "tree" : "blob" | 156 | + File.directory?(path_on_fs(path)) ? "tree" : "blob" |
| 157 | + end | ||
| 158 | + | ||
| 159 | + # Path to the file in the satellites repository on the filesystem | ||
| 160 | + def path_on_fs(path) | ||
| 161 | + [@path_to_satellite, path].join("/") | ||
| 155 | end | 162 | end |
| 156 | 163 | ||
| 157 | # We will assume that if no ref exists we can point to master | 164 | # We will assume that if no ref exists we can point to master |
app/helpers/groups_helper.rb
| @@ -2,4 +2,23 @@ module GroupsHelper | @@ -2,4 +2,23 @@ module GroupsHelper | ||
| 2 | def remove_user_from_group_message(group, user) | 2 | def remove_user_from_group_message(group, user) |
| 3 | "You are going to remove #{user.name} from #{group.name} Group. Are you sure?" | 3 | "You are going to remove #{user.name} from #{group.name} Group. Are you sure?" |
| 4 | end | 4 | end |
| 5 | + | ||
| 6 | + def group_head_title | ||
| 7 | + title = @group.name | ||
| 8 | + | ||
| 9 | + title = if current_action?(:issues) | ||
| 10 | + "Issues - " + title | ||
| 11 | + elsif current_action?(:merge_requests) | ||
| 12 | + "Merge requests - " + title | ||
| 13 | + elsif current_action?(:members) | ||
| 14 | + "Members - " + title | ||
| 15 | + elsif current_action?(:edit) | ||
| 16 | + "Settings - " + title | ||
| 17 | + else | ||
| 18 | + title | ||
| 19 | + end | ||
| 20 | + | ||
| 21 | + title | ||
| 22 | + | ||
| 23 | + end | ||
| 5 | end | 24 | end |
| @@ -0,0 +1,21 @@ | @@ -0,0 +1,21 @@ | ||
| 1 | +module IconsHelper | ||
| 2 | + def boolean_to_icon(value) | ||
| 3 | + if value.to_s == "true" | ||
| 4 | + content_tag :i, nil, class: 'icon-ok cgreen' | ||
| 5 | + else | ||
| 6 | + content_tag :i, nil, class: 'icon-off clgray' | ||
| 7 | + end | ||
| 8 | + end | ||
| 9 | + | ||
| 10 | + def public_icon | ||
| 11 | + content_tag :i, nil, class: 'icon-globe' | ||
| 12 | + end | ||
| 13 | + | ||
| 14 | + def internal_icon | ||
| 15 | + content_tag :i, nil, class: 'icon-shield' | ||
| 16 | + end | ||
| 17 | + | ||
| 18 | + def private_icon | ||
| 19 | + content_tag :i, nil, class: 'icon-lock' | ||
| 20 | + end | ||
| 21 | +end |
app/helpers/issues_helper.rb
| @@ -68,4 +68,12 @@ module IssuesHelper | @@ -68,4 +68,12 @@ module IssuesHelper | ||
| 68 | false | 68 | false |
| 69 | end | 69 | end |
| 70 | end | 70 | end |
| 71 | + | ||
| 72 | + def bulk_update_milestone_options | ||
| 73 | + options_for_select(["None (backlog)", nil]) + options_from_collection_for_select(project_active_milestones, "id", "title", params[:milestone_id]) | ||
| 74 | + end | ||
| 75 | + | ||
| 76 | + def bulk_update_assignee_options | ||
| 77 | + options_for_select(["None (unassigned)", nil]) + options_from_collection_for_select(@project.team.members, "id", "name", params[:assignee_id]) | ||
| 78 | + end | ||
| 71 | end | 79 | end |
app/helpers/namespaces_helper.rb
| @@ -16,4 +16,13 @@ module NamespacesHelper | @@ -16,4 +16,13 @@ module NamespacesHelper | ||
| 16 | 16 | ||
| 17 | grouped_options_for_select(options, selected) | 17 | grouped_options_for_select(options, selected) |
| 18 | end | 18 | end |
| 19 | + | ||
| 20 | + def namespace_select_tag(id, opts = {}) | ||
| 21 | + css_class = "ajax-namespace-select " | ||
| 22 | + css_class << "multiselect " if opts[:multiple] | ||
| 23 | + css_class << (opts[:class] || '') | ||
| 24 | + value = opts[:selected] || '' | ||
| 25 | + | ||
| 26 | + hidden_field_tag(id, value, class: css_class) | ||
| 27 | + end | ||
| 19 | end | 28 | end |
app/helpers/projects_helper.rb
| @@ -70,6 +70,8 @@ module ProjectsHelper | @@ -70,6 +70,8 @@ module ProjectsHelper | ||
| 70 | scope: params[:scope], | 70 | scope: params[:scope], |
| 71 | label_name: params[:label_name], | 71 | label_name: params[:label_name], |
| 72 | milestone_id: params[:milestone_id], | 72 | milestone_id: params[:milestone_id], |
| 73 | + assignee_id: params[:assignee_id], | ||
| 74 | + sort: params[:sort], | ||
| 73 | } | 75 | } |
| 74 | 76 | ||
| 75 | options = exist_opts.merge(options) | 77 | options = exist_opts.merge(options) |
| @@ -135,12 +137,46 @@ module ProjectsHelper | @@ -135,12 +137,46 @@ module ProjectsHelper | ||
| 135 | end | 137 | end |
| 136 | end | 138 | end |
| 137 | 139 | ||
| 138 | - def repository_size | ||
| 139 | - "#{@project.repository.size} MB" | 140 | + def repository_size(project = nil) |
| 141 | + "#{(project || @project).repository.size} MB" | ||
| 140 | rescue | 142 | rescue |
| 141 | # In order to prevent 500 error | 143 | # In order to prevent 500 error |
| 142 | # when application cannot allocate memory | 144 | # when application cannot allocate memory |
| 143 | # to calculate repo size - just show 'Unknown' | 145 | # to calculate repo size - just show 'Unknown' |
| 144 | 'unknown' | 146 | 'unknown' |
| 145 | end | 147 | end |
| 148 | + | ||
| 149 | + def project_head_title | ||
| 150 | + title = @project.name_with_namespace | ||
| 151 | + | ||
| 152 | + title = if current_controller?(:tree) | ||
| 153 | + "#{@project.path}\/#{@path} at #{@ref} - " + title | ||
| 154 | + elsif current_controller?(:issues) | ||
| 155 | + if current_action?(:show) | ||
| 156 | + "Issue ##{@issue.iid} - " + title | ||
| 157 | + else | ||
| 158 | + "Issues - " + title | ||
| 159 | + end | ||
| 160 | + elsif current_controller?(:blob) | ||
| 161 | + "#{@project.path}\/#{@blob.path} at #{@ref} - " + title | ||
| 162 | + elsif current_controller?(:commits) | ||
| 163 | + "Commits at #{@ref} - " + title | ||
| 164 | + elsif current_controller?(:merge_requests) | ||
| 165 | + if current_action?(:show) | ||
| 166 | + "Merge request ##{@merge_request.iid} - " + title | ||
| 167 | + else | ||
| 168 | + "Merge requests - " + title | ||
| 169 | + end | ||
| 170 | + elsif current_controller?(:wikis) | ||
| 171 | + "Wiki - " + title | ||
| 172 | + elsif current_controller?(:network) | ||
| 173 | + "Network graph - " + title | ||
| 174 | + elsif current_controller?(:graphs) | ||
| 175 | + "Graphs - " + title | ||
| 176 | + else | ||
| 177 | + title | ||
| 178 | + end | ||
| 179 | + | ||
| 180 | + title | ||
| 181 | + end | ||
| 146 | end | 182 | end |
app/helpers/search_helper.rb
| 1 | module SearchHelper | 1 | module SearchHelper |
| 2 | def search_autocomplete_source | 2 | def search_autocomplete_source |
| 3 | return unless current_user | 3 | return unless current_user |
| 4 | - | ||
| 5 | [ | 4 | [ |
| 6 | groups_autocomplete, | 5 | groups_autocomplete, |
| 7 | projects_autocomplete, | 6 | projects_autocomplete, |
| 7 | + public_projects_autocomplete, | ||
| 8 | default_autocomplete, | 8 | default_autocomplete, |
| 9 | project_autocomplete, | 9 | project_autocomplete, |
| 10 | help_autocomplete | 10 | help_autocomplete |
| @@ -75,4 +75,11 @@ module SearchHelper | @@ -75,4 +75,11 @@ module SearchHelper | ||
| 75 | { label: "project: #{simple_sanitize(p.name_with_namespace)}", url: project_path(p) } | 75 | { label: "project: #{simple_sanitize(p.name_with_namespace)}", url: project_path(p) } |
| 76 | end | 76 | end |
| 77 | end | 77 | end |
| 78 | + | ||
| 79 | + # Autocomplete results for the current user's projects | ||
| 80 | + def public_projects_autocomplete | ||
| 81 | + Project.public_or_internal_only(current_user).map do |p| | ||
| 82 | + { label: "project: #{simple_sanitize(p.name_with_namespace)}", url: project_path(p) } | ||
| 83 | + end | ||
| 84 | + end | ||
| 78 | end | 85 | end |
| @@ -0,0 +1,49 @@ | @@ -0,0 +1,49 @@ | ||
| 1 | +module VisibilityLevelHelper | ||
| 2 | + def visibility_level_color(level) | ||
| 3 | + case level | ||
| 4 | + when Gitlab::VisibilityLevel::PRIVATE | ||
| 5 | + 'cgreen' | ||
| 6 | + when Gitlab::VisibilityLevel::INTERNAL | ||
| 7 | + 'camber' | ||
| 8 | + when Gitlab::VisibilityLevel::PUBLIC | ||
| 9 | + 'cblue' | ||
| 10 | + end | ||
| 11 | + end | ||
| 12 | + | ||
| 13 | + def visibility_level_description(level) | ||
| 14 | + capture_haml do | ||
| 15 | + haml_tag :span do | ||
| 16 | + case level | ||
| 17 | + when Gitlab::VisibilityLevel::PRIVATE | ||
| 18 | + haml_concat "Project access must be granted explicitly for each user." | ||
| 19 | + when Gitlab::VisibilityLevel::INTERNAL | ||
| 20 | + haml_concat "The project can be cloned by" | ||
| 21 | + haml_concat "any logged in user." | ||
| 22 | + when Gitlab::VisibilityLevel::PUBLIC | ||
| 23 | + haml_concat "The project can be cloned" | ||
| 24 | + haml_concat "without any" | ||
| 25 | + haml_concat "authentication." | ||
| 26 | + end | ||
| 27 | + end | ||
| 28 | + end | ||
| 29 | + end | ||
| 30 | + | ||
| 31 | + def visibility_level_icon(level) | ||
| 32 | + case level | ||
| 33 | + when Gitlab::VisibilityLevel::PRIVATE | ||
| 34 | + private_icon | ||
| 35 | + when Gitlab::VisibilityLevel::INTERNAL | ||
| 36 | + internal_icon | ||
| 37 | + when Gitlab::VisibilityLevel::PUBLIC | ||
| 38 | + public_icon | ||
| 39 | + end | ||
| 40 | + end | ||
| 41 | + | ||
| 42 | + def visibility_level_label(level) | ||
| 43 | + Project.visibility_levels.key(level) | ||
| 44 | + end | ||
| 45 | + | ||
| 46 | + def restricted_visibility_levels | ||
| 47 | + current_user.is_admin? ? [] : gitlab_config.restricted_visibility_levels | ||
| 48 | + end | ||
| 49 | +end |
app/mailers/emails/groups.rb
| @@ -5,7 +5,7 @@ module Emails | @@ -5,7 +5,7 @@ module Emails | ||
| 5 | @group = @membership.group | 5 | @group = @membership.group |
| 6 | 6 | ||
| 7 | mail(to: @membership.user.email, | 7 | mail(to: @membership.user.email, |
| 8 | - subject: subject("access to group was granted")) | 8 | + subject: subject("Access to group was granted")) |
| 9 | end | 9 | end |
| 10 | end | 10 | end |
| 11 | end | 11 | end |
app/mailers/emails/issues.rb
| @@ -3,14 +3,14 @@ module Emails | @@ -3,14 +3,14 @@ module Emails | ||
| 3 | def new_issue_email(recipient_id, issue_id) | 3 | def new_issue_email(recipient_id, issue_id) |
| 4 | @issue = Issue.find(issue_id) | 4 | @issue = Issue.find(issue_id) |
| 5 | @project = @issue.project | 5 | @project = @issue.project |
| 6 | - mail(to: recipient(recipient_id), subject: subject("new issue ##{@issue.iid}", @issue.title)) | 6 | + mail(to: recipient(recipient_id), subject: subject("New issue ##{@issue.iid}", @issue.title)) |
| 7 | end | 7 | end |
| 8 | 8 | ||
| 9 | def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id) | 9 | def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id) |
| 10 | @issue = Issue.find(issue_id) | 10 | @issue = Issue.find(issue_id) |
| 11 | @previous_assignee = User.find_by_id(previous_assignee_id) if previous_assignee_id | 11 | @previous_assignee = User.find_by_id(previous_assignee_id) if previous_assignee_id |
| 12 | @project = @issue.project | 12 | @project = @issue.project |
| 13 | - mail(to: recipient(recipient_id), subject: subject("changed issue ##{@issue.iid}", @issue.title)) | 13 | + mail(to: recipient(recipient_id), subject: subject("Changed issue ##{@issue.iid}", @issue.title)) |
| 14 | end | 14 | end |
| 15 | 15 | ||
| 16 | def closed_issue_email(recipient_id, issue_id, updated_by_user_id) | 16 | def closed_issue_email(recipient_id, issue_id, updated_by_user_id) |
| @@ -27,7 +27,7 @@ module Emails | @@ -27,7 +27,7 @@ module Emails | ||
| 27 | @project = @issue.project | 27 | @project = @issue.project |
| 28 | @updated_by = User.find updated_by_user_id | 28 | @updated_by = User.find updated_by_user_id |
| 29 | mail(to: recipient(recipient_id), | 29 | mail(to: recipient(recipient_id), |
| 30 | - subject: subject("changed issue ##{@issue.iid}", @issue.title)) | 30 | + subject: subject("Changed issue ##{@issue.iid}", @issue.title)) |
| 31 | end | 31 | end |
| 32 | end | 32 | end |
| 33 | end | 33 | end |
app/mailers/emails/merge_requests.rb
| @@ -2,24 +2,24 @@ module Emails | @@ -2,24 +2,24 @@ module Emails | ||
| 2 | module MergeRequests | 2 | module MergeRequests |
| 3 | def new_merge_request_email(recipient_id, merge_request_id) | 3 | def new_merge_request_email(recipient_id, merge_request_id) |
| 4 | @merge_request = MergeRequest.find(merge_request_id) | 4 | @merge_request = MergeRequest.find(merge_request_id) |
| 5 | - mail(to: recipient(recipient_id), subject: subject("new merge request !#{@merge_request.iid}", @merge_request.title)) | 5 | + mail(to: recipient(recipient_id), subject: subject("New merge request ##{@merge_request.iid}", @merge_request.title)) |
| 6 | end | 6 | end |
| 7 | 7 | ||
| 8 | def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id) | 8 | def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id) |
| 9 | @merge_request = MergeRequest.find(merge_request_id) | 9 | @merge_request = MergeRequest.find(merge_request_id) |
| 10 | @previous_assignee = User.find_by_id(previous_assignee_id) if previous_assignee_id | 10 | @previous_assignee = User.find_by_id(previous_assignee_id) if previous_assignee_id |
| 11 | - mail(to: recipient(recipient_id), subject: subject("changed merge request !#{@merge_request.iid}", @merge_request.title)) | 11 | + mail(to: recipient(recipient_id), subject: subject("Changed merge request ##{@merge_request.iid}", @merge_request.title)) |
| 12 | end | 12 | end |
| 13 | 13 | ||
| 14 | def closed_merge_request_email(recipient_id, merge_request_id, updated_by_user_id) | 14 | def closed_merge_request_email(recipient_id, merge_request_id, updated_by_user_id) |
| 15 | @merge_request = MergeRequest.find(merge_request_id) | 15 | @merge_request = MergeRequest.find(merge_request_id) |
| 16 | @updated_by = User.find updated_by_user_id | 16 | @updated_by = User.find updated_by_user_id |
| 17 | - mail(to: recipient(recipient_id), subject: subject("Closed merge request !#{@merge_request.iid}", @merge_request.title)) | 17 | + mail(to: recipient(recipient_id), subject: subject("Closed merge request ##{@merge_request.iid}", @merge_request.title)) |
| 18 | end | 18 | end |
| 19 | 19 | ||
| 20 | def merged_merge_request_email(recipient_id, merge_request_id) | 20 | def merged_merge_request_email(recipient_id, merge_request_id) |
| 21 | @merge_request = MergeRequest.find(merge_request_id) | 21 | @merge_request = MergeRequest.find(merge_request_id) |
| 22 | - mail(to: recipient(recipient_id), subject: subject("Accepted merge request !#{@merge_request.iid}", @merge_request.title)) | 22 | + mail(to: recipient(recipient_id), subject: subject("Accepted merge request ##{@merge_request.iid}", @merge_request.title)) |
| 23 | end | 23 | end |
| 24 | end | 24 | end |
| 25 | 25 |
app/mailers/emails/notes.rb
| @@ -4,27 +4,27 @@ module Emails | @@ -4,27 +4,27 @@ module Emails | ||
| 4 | @note = Note.find(note_id) | 4 | @note = Note.find(note_id) |
| 5 | @commit = @note.noteable | 5 | @commit = @note.noteable |
| 6 | @project = @note.project | 6 | @project = @note.project |
| 7 | - mail(to: recipient(recipient_id), subject: subject("note for commit #{@commit.short_id}", @commit.title)) | 7 | + mail(to: recipient(recipient_id), subject: subject("Note for commit #{@commit.short_id}", @commit.title)) |
| 8 | end | 8 | end |
| 9 | 9 | ||
| 10 | def note_issue_email(recipient_id, note_id) | 10 | def note_issue_email(recipient_id, note_id) |
| 11 | @note = Note.find(note_id) | 11 | @note = Note.find(note_id) |
| 12 | @issue = @note.noteable | 12 | @issue = @note.noteable |
| 13 | @project = @note.project | 13 | @project = @note.project |
| 14 | - mail(to: recipient(recipient_id), subject: subject("note for issue ##{@issue.iid}")) | 14 | + mail(to: recipient(recipient_id), subject: subject("Note for issue ##{@issue.iid}")) |
| 15 | end | 15 | end |
| 16 | 16 | ||
| 17 | def note_merge_request_email(recipient_id, note_id) | 17 | def note_merge_request_email(recipient_id, note_id) |
| 18 | @note = Note.find(note_id) | 18 | @note = Note.find(note_id) |
| 19 | @merge_request = @note.noteable | 19 | @merge_request = @note.noteable |
| 20 | @project = @note.project | 20 | @project = @note.project |
| 21 | - mail(to: recipient(recipient_id), subject: subject("note for merge request ##{@merge_request.iid}")) | 21 | + mail(to: recipient(recipient_id), subject: subject("Note for merge request ##{@merge_request.iid}")) |
| 22 | end | 22 | end |
| 23 | 23 | ||
| 24 | def note_wall_email(recipient_id, note_id) | 24 | def note_wall_email(recipient_id, note_id) |
| 25 | @note = Note.find(note_id) | 25 | @note = Note.find(note_id) |
| 26 | @project = @note.project | 26 | @project = @note.project |
| 27 | - mail(to: recipient(recipient_id), subject: subject("note on wall")) | 27 | + mail(to: recipient(recipient_id), subject: subject("Note on wall")) |
| 28 | end | 28 | end |
| 29 | end | 29 | end |
| 30 | end | 30 | end |
app/mailers/emails/projects.rb
| @@ -4,14 +4,14 @@ module Emails | @@ -4,14 +4,14 @@ module Emails | ||
| 4 | @users_project = UsersProject.find user_project_id | 4 | @users_project = UsersProject.find user_project_id |
| 5 | @project = @users_project.project | 5 | @project = @users_project.project |
| 6 | mail(to: @users_project.user.email, | 6 | mail(to: @users_project.user.email, |
| 7 | - subject: subject("access to project was granted")) | 7 | + subject: subject("Access to project was granted")) |
| 8 | end | 8 | end |
| 9 | 9 | ||
| 10 | def project_was_moved_email(project_id, user_id) | 10 | def project_was_moved_email(project_id, user_id) |
| 11 | @user = User.find user_id | 11 | @user = User.find user_id |
| 12 | @project = Project.find project_id | 12 | @project = Project.find project_id |
| 13 | mail(to: @user.email, | 13 | mail(to: @user.email, |
| 14 | - subject: subject("project was moved")) | 14 | + subject: subject("Project was moved")) |
| 15 | end | 15 | end |
| 16 | end | 16 | end |
| 17 | end | 17 | end |
app/models/ability.rb
| @@ -29,7 +29,7 @@ class Ability | @@ -29,7 +29,7 @@ class Ability | ||
| 29 | nil | 29 | nil |
| 30 | end | 30 | end |
| 31 | 31 | ||
| 32 | - if project && project.public | 32 | + if project && project.public? |
| 33 | [ | 33 | [ |
| 34 | :read_project, | 34 | :read_project, |
| 35 | :read_wiki, | 35 | :read_wiki, |
| @@ -71,7 +71,7 @@ class Ability | @@ -71,7 +71,7 @@ class Ability | ||
| 71 | rules << project_guest_rules | 71 | rules << project_guest_rules |
| 72 | end | 72 | end |
| 73 | 73 | ||
| 74 | - if project.public? | 74 | + if project.public? || project.internal? |
| 75 | rules << public_project_rules | 75 | rules << public_project_rules |
| 76 | end | 76 | end |
| 77 | 77 | ||
| @@ -89,7 +89,7 @@ class Ability | @@ -89,7 +89,7 @@ class Ability | ||
| 89 | def public_project_rules | 89 | def public_project_rules |
| 90 | project_guest_rules + [ | 90 | project_guest_rules + [ |
| 91 | :download_code, | 91 | :download_code, |
| 92 | - :fork_project, | 92 | + :fork_project |
| 93 | ] | 93 | ] |
| 94 | end | 94 | end |
| 95 | 95 | ||
| @@ -145,7 +145,7 @@ class Ability | @@ -145,7 +145,7 @@ class Ability | ||
| 145 | def project_admin_rules | 145 | def project_admin_rules |
| 146 | project_master_rules + [ | 146 | project_master_rules + [ |
| 147 | :change_namespace, | 147 | :change_namespace, |
| 148 | - :change_public_mode, | 148 | + :change_visibility_level, |
| 149 | :rename_project, | 149 | :rename_project, |
| 150 | :remove_project | 150 | :remove_project |
| 151 | ] | 151 | ] |
| @@ -0,0 +1,45 @@ | @@ -0,0 +1,45 @@ | ||
| 1 | +# == Schema Information | ||
| 2 | +# | ||
| 3 | +# Table name: services | ||
| 4 | +# | ||
| 5 | +# id :integer not null, primary key | ||
| 6 | +# type :string(255) | ||
| 7 | +# title :string(255) | ||
| 8 | +# token :string(255) | ||
| 9 | +# project_id :integer not null | ||
| 10 | +# created_at :datetime not null | ||
| 11 | +# updated_at :datetime not null | ||
| 12 | +# active :boolean default(FALSE), not null | ||
| 13 | +# project_url :string(255) | ||
| 14 | +# subdomain :string(255) | ||
| 15 | +# room :string(255) | ||
| 16 | +# | ||
| 17 | + | ||
| 18 | +class AssemblaService < Service | ||
| 19 | + include HTTParty | ||
| 20 | + | ||
| 21 | + validates :token, presence: true, if: :activated? | ||
| 22 | + | ||
| 23 | + def title | ||
| 24 | + 'Assembla' | ||
| 25 | + end | ||
| 26 | + | ||
| 27 | + def description | ||
| 28 | + 'Project Management Software (Source Commits Endpoint)' | ||
| 29 | + end | ||
| 30 | + | ||
| 31 | + def to_param | ||
| 32 | + 'assembla' | ||
| 33 | + end | ||
| 34 | + | ||
| 35 | + def fields | ||
| 36 | + [ | ||
| 37 | + { type: 'text', name: 'token', placeholder: '' } | ||
| 38 | + ] | ||
| 39 | + end | ||
| 40 | + | ||
| 41 | + def execute(push) | ||
| 42 | + url = "https://atlas.assembla.com/spaces/ouposp/github_tool?secret_key=#{token}" | ||
| 43 | + AssemblaService.post(url, body: { payload: push }.to_json, headers: { 'Content-Type' => 'application/json' }) | ||
| 44 | + end | ||
| 45 | +end |
| @@ -0,0 +1,24 @@ | @@ -0,0 +1,24 @@ | ||
| 1 | +# == Schema Information | ||
| 2 | +# | ||
| 3 | +# Table name: broadcast_messages | ||
| 4 | +# | ||
| 5 | +# id :integer not null, primary key | ||
| 6 | +# message :text default(""), not null | ||
| 7 | +# starts_at :datetime | ||
| 8 | +# ends_at :datetime | ||
| 9 | +# alert_type :integer | ||
| 10 | +# created_at :datetime not null | ||
| 11 | +# updated_at :datetime not null | ||
| 12 | +# | ||
| 13 | + | ||
| 14 | +class BroadcastMessage < ActiveRecord::Base | ||
| 15 | + attr_accessible :alert_type, :ends_at, :message, :starts_at | ||
| 16 | + | ||
| 17 | + validates :message, presence: true | ||
| 18 | + validates :starts_at, presence: true | ||
| 19 | + validates :ends_at, presence: true | ||
| 20 | + | ||
| 21 | + def self.current | ||
| 22 | + where("ends_at > :now AND starts_at < :now", now: Time.zone.now).last | ||
| 23 | + end | ||
| 24 | +end |
app/models/concerns/issuable.rb
| @@ -111,4 +111,11 @@ module Issuable | @@ -111,4 +111,11 @@ module Issuable | ||
| 111 | end | 111 | end |
| 112 | users.concat(mentions.reduce([], :|)).uniq | 112 | users.concat(mentions.reduce([], :|)).uniq |
| 113 | end | 113 | end |
| 114 | + | ||
| 115 | + def to_hook_data | ||
| 116 | + { | ||
| 117 | + object_kind: self.class.name.underscore, | ||
| 118 | + object_attributes: self.attributes | ||
| 119 | + } | ||
| 120 | + end | ||
| 114 | end | 121 | end |
app/models/event.rb
| @@ -168,7 +168,7 @@ class Event < ActiveRecord::Base | @@ -168,7 +168,7 @@ class Event < ActiveRecord::Base | ||
| 168 | end | 168 | end |
| 169 | 169 | ||
| 170 | def valid_push? | 170 | def valid_push? |
| 171 | - data[:ref] | 171 | + data[:ref] && ref_name.present? |
| 172 | rescue => ex | 172 | rescue => ex |
| 173 | false | 173 | false |
| 174 | end | 174 | end |
| @@ -223,7 +223,7 @@ class Event < ActiveRecord::Base | @@ -223,7 +223,7 @@ class Event < ActiveRecord::Base | ||
| 223 | 223 | ||
| 224 | # Max 20 commits from push DESC | 224 | # Max 20 commits from push DESC |
| 225 | def commits | 225 | def commits |
| 226 | - @commits ||= data[:commits].reverse | 226 | + @commits ||= (data[:commits] || []).reverse |
| 227 | end | 227 | end |
| 228 | 228 | ||
| 229 | def commits_count | 229 | def commits_count |
app/models/flowdock_service.rb
| @@ -11,6 +11,8 @@ | @@ -11,6 +11,8 @@ | ||
| 11 | # updated_at :datetime not null | 11 | # updated_at :datetime not null |
| 12 | # active :boolean default(FALSE), not null | 12 | # active :boolean default(FALSE), not null |
| 13 | # project_url :string(255) | 13 | # project_url :string(255) |
| 14 | +# subdomain :string(255) | ||
| 15 | +# room :string(255) | ||
| 14 | # | 16 | # |
| 15 | 17 | ||
| 16 | require "flowdock-git-hook" | 18 | require "flowdock-git-hook" |
app/models/gollum_wiki.rb
| @@ -33,7 +33,7 @@ class GollumWiki | @@ -33,7 +33,7 @@ class GollumWiki | ||
| 33 | end | 33 | end |
| 34 | 34 | ||
| 35 | def http_url_to_repo | 35 | def http_url_to_repo |
| 36 | - http_url = [Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('') | 36 | + [Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('') |
| 37 | end | 37 | end |
| 38 | 38 | ||
| 39 | # Returns the Gollum::Wiki object. | 39 | # Returns the Gollum::Wiki object. |
app/models/hipchat_service.rb
app/models/issue.rb
| @@ -21,6 +21,8 @@ class Issue < ActiveRecord::Base | @@ -21,6 +21,8 @@ class Issue < ActiveRecord::Base | ||
| 21 | include Issuable | 21 | include Issuable |
| 22 | include InternalId | 22 | include InternalId |
| 23 | 23 | ||
| 24 | + ActsAsTaggableOn.strict_case_match = true | ||
| 25 | + | ||
| 24 | belongs_to :project | 26 | belongs_to :project |
| 25 | validates :project, presence: true | 27 | validates :project, presence: true |
| 26 | 28 |
app/models/namespace.rb
| @@ -87,4 +87,8 @@ class Namespace < ActiveRecord::Base | @@ -87,4 +87,8 @@ class Namespace < ActiveRecord::Base | ||
| 87 | def send_update_instructions | 87 | def send_update_instructions |
| 88 | projects.each(&:send_move_instructions) | 88 | projects.each(&:send_move_instructions) |
| 89 | end | 89 | end |
| 90 | + | ||
| 91 | + def kind | ||
| 92 | + type == 'Group' ? 'group' : 'user' | ||
| 93 | + end | ||
| 90 | end | 94 | end |
app/models/note.rb
| @@ -157,7 +157,8 @@ class Note < ActiveRecord::Base | @@ -157,7 +157,8 @@ class Note < ActiveRecord::Base | ||
| 157 | # otherwise false is returned | 157 | # otherwise false is returned |
| 158 | def downvote? | 158 | def downvote? |
| 159 | votable? && (note.start_with?('-1') || | 159 | votable? && (note.start_with?('-1') || |
| 160 | - note.start_with?(':-1:') | 160 | + note.start_with?(':-1:') || |
| 161 | + note.start_with?(':thumbsdown:') | ||
| 161 | ) | 162 | ) |
| 162 | end | 163 | end |
| 163 | 164 | ||
| @@ -206,7 +207,8 @@ class Note < ActiveRecord::Base | @@ -206,7 +207,8 @@ class Note < ActiveRecord::Base | ||
| 206 | # otherwise false is returned | 207 | # otherwise false is returned |
| 207 | def upvote? | 208 | def upvote? |
| 208 | votable? && (note.start_with?('+1') || | 209 | votable? && (note.start_with?('+1') || |
| 209 | - note.start_with?(':+1:') | 210 | + note.start_with?(':+1:') || |
| 211 | + note.start_with?(':thumbsup:') | ||
| 210 | ) | 212 | ) |
| 211 | end | 213 | end |
| 212 | 214 |
app/models/project.rb
| @@ -9,33 +9,37 @@ | @@ -9,33 +9,37 @@ | ||
| 9 | # created_at :datetime not null | 9 | # created_at :datetime not null |
| 10 | # updated_at :datetime not null | 10 | # updated_at :datetime not null |
| 11 | # creator_id :integer | 11 | # creator_id :integer |
| 12 | -# default_branch :string(255) | ||
| 13 | # issues_enabled :boolean default(TRUE), not null | 12 | # issues_enabled :boolean default(TRUE), not null |
| 14 | # wall_enabled :boolean default(TRUE), not null | 13 | # wall_enabled :boolean default(TRUE), not null |
| 15 | # merge_requests_enabled :boolean default(TRUE), not null | 14 | # merge_requests_enabled :boolean default(TRUE), not null |
| 16 | # wiki_enabled :boolean default(TRUE), not null | 15 | # wiki_enabled :boolean default(TRUE), not null |
| 17 | # namespace_id :integer | 16 | # namespace_id :integer |
| 18 | -# public :boolean default(FALSE), not null | ||
| 19 | # issues_tracker :string(255) default("gitlab"), not null | 17 | # issues_tracker :string(255) default("gitlab"), not null |
| 20 | # issues_tracker_id :string(255) | 18 | # issues_tracker_id :string(255) |
| 21 | # snippets_enabled :boolean default(TRUE), not null | 19 | # snippets_enabled :boolean default(TRUE), not null |
| 22 | # last_activity_at :datetime | 20 | # last_activity_at :datetime |
| 23 | # imported :boolean default(FALSE), not null | 21 | # imported :boolean default(FALSE), not null |
| 24 | # import_url :string(255) | 22 | # import_url :string(255) |
| 23 | +# visibility_level :integer default(0), not null | ||
| 25 | # | 24 | # |
| 26 | 25 | ||
| 27 | class Project < ActiveRecord::Base | 26 | class Project < ActiveRecord::Base |
| 28 | include Gitlab::ShellAdapter | 27 | include Gitlab::ShellAdapter |
| 28 | + include Gitlab::VisibilityLevel | ||
| 29 | extend Enumerize | 29 | extend Enumerize |
| 30 | 30 | ||
| 31 | - attr_accessible :name, :path, :description, :default_branch, :issues_tracker, :label_list, | 31 | + ActsAsTaggableOn.strict_case_match = true |
| 32 | + | ||
| 33 | + attr_accessible :name, :path, :description, :issues_tracker, :label_list, | ||
| 32 | :issues_enabled, :wall_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id, | 34 | :issues_enabled, :wall_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id, |
| 33 | - :wiki_enabled, :public, :import_url, :last_activity_at, as: [:default, :admin] | 35 | + :wiki_enabled, :visibility_level, :import_url, :last_activity_at, as: [:default, :admin] |
| 34 | 36 | ||
| 35 | attr_accessible :namespace_id, :creator_id, as: :admin | 37 | attr_accessible :namespace_id, :creator_id, as: :admin |
| 36 | 38 | ||
| 37 | acts_as_taggable_on :labels, :issues_default_labels | 39 | acts_as_taggable_on :labels, :issues_default_labels |
| 38 | 40 | ||
| 41 | + attr_accessor :new_default_branch | ||
| 42 | + | ||
| 39 | # Relations | 43 | # Relations |
| 40 | belongs_to :creator, foreign_key: "creator_id", class_name: "User" | 44 | belongs_to :creator, foreign_key: "creator_id", class_name: "User" |
| 41 | belongs_to :group, foreign_key: "namespace_id", conditions: "type = 'Group'" | 45 | belongs_to :group, foreign_key: "namespace_id", conditions: "type = 'Group'" |
| @@ -47,6 +51,7 @@ class Project < ActiveRecord::Base | @@ -47,6 +51,7 @@ class Project < ActiveRecord::Base | ||
| 47 | has_one :pivotaltracker_service, dependent: :destroy | 51 | has_one :pivotaltracker_service, dependent: :destroy |
| 48 | has_one :hipchat_service, dependent: :destroy | 52 | has_one :hipchat_service, dependent: :destroy |
| 49 | has_one :flowdock_service, dependent: :destroy | 53 | has_one :flowdock_service, dependent: :destroy |
| 54 | + has_one :assembla_service, dependent: :destroy | ||
| 50 | has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id" | 55 | has_one :forked_project_link, dependent: :destroy, foreign_key: "forked_to_project_id" |
| 51 | has_one :forked_from_project, through: :forked_project_link | 56 | has_one :forked_from_project, through: :forked_project_link |
| 52 | 57 | ||
| @@ -104,7 +109,8 @@ class Project < ActiveRecord::Base | @@ -104,7 +109,8 @@ class Project < ActiveRecord::Base | ||
| 104 | scope :sorted_by_activity, -> { reorder("projects.last_activity_at DESC") } | 109 | scope :sorted_by_activity, -> { reorder("projects.last_activity_at DESC") } |
| 105 | scope :personal, ->(user) { where(namespace_id: user.namespace_id) } | 110 | scope :personal, ->(user) { where(namespace_id: user.namespace_id) } |
| 106 | scope :joined, ->(user) { where("namespace_id != ?", user.namespace_id) } | 111 | scope :joined, ->(user) { where("namespace_id != ?", user.namespace_id) } |
| 107 | - scope :public_only, -> { where(public: true) } | 112 | + scope :public_only, -> { where(visibility_level: PUBLIC) } |
| 113 | + scope :public_or_internal_only, ->(user) { where("visibility_level IN (:levels)", levels: user ? [ INTERNAL, PUBLIC ] : [ PUBLIC ]) } | ||
| 108 | 114 | ||
| 109 | enumerize :issues_tracker, in: (Gitlab.config.issues_tracker.keys).append(:gitlab), default: :gitlab | 115 | enumerize :issues_tracker, in: (Gitlab.config.issues_tracker.keys).append(:gitlab), default: :gitlab |
| 110 | 116 | ||
| @@ -136,6 +142,10 @@ class Project < ActiveRecord::Base | @@ -136,6 +142,10 @@ class Project < ActiveRecord::Base | ||
| 136 | where(path: id, namespace_id: nil).last | 142 | where(path: id, namespace_id: nil).last |
| 137 | end | 143 | end |
| 138 | end | 144 | end |
| 145 | + | ||
| 146 | + def visibility_levels | ||
| 147 | + Gitlab::VisibilityLevel.options | ||
| 148 | + end | ||
| 139 | end | 149 | end |
| 140 | 150 | ||
| 141 | def team | 151 | def team |
| @@ -143,7 +153,7 @@ class Project < ActiveRecord::Base | @@ -143,7 +153,7 @@ class Project < ActiveRecord::Base | ||
| 143 | end | 153 | end |
| 144 | 154 | ||
| 145 | def repository | 155 | def repository |
| 146 | - @repository ||= Repository.new(path_with_namespace, default_branch) | 156 | + @repository ||= Repository.new(path_with_namespace) |
| 147 | end | 157 | end |
| 148 | 158 | ||
| 149 | def saved? | 159 | def saved? |
| @@ -221,7 +231,7 @@ class Project < ActiveRecord::Base | @@ -221,7 +231,7 @@ class Project < ActiveRecord::Base | ||
| 221 | end | 231 | end |
| 222 | 232 | ||
| 223 | def available_services_names | 233 | def available_services_names |
| 224 | - %w(gitlab_ci campfire hipchat pivotaltracker flowdock) | 234 | + %w(gitlab_ci campfire hipchat pivotaltracker flowdock assembla) |
| 225 | end | 235 | end |
| 226 | 236 | ||
| 227 | def gitlab_ci? | 237 | def gitlab_ci? |
| @@ -288,8 +298,10 @@ class Project < ActiveRecord::Base | @@ -288,8 +298,10 @@ class Project < ActiveRecord::Base | ||
| 288 | ProjectTransferService.new.transfer(self, new_namespace) | 298 | ProjectTransferService.new.transfer(self, new_namespace) |
| 289 | end | 299 | end |
| 290 | 300 | ||
| 291 | - def execute_hooks(data) | ||
| 292 | - hooks.each { |hook| hook.async_execute(data) } | 301 | + def execute_hooks(data, hooks_scope = :push_hooks) |
| 302 | + hooks.send(hooks_scope).each do |hook| | ||
| 303 | + hook.async_execute(data) | ||
| 304 | + end | ||
| 293 | end | 305 | end |
| 294 | 306 | ||
| 295 | def execute_services(data) | 307 | def execute_services(data) |
| @@ -300,14 +312,6 @@ class Project < ActiveRecord::Base | @@ -300,14 +312,6 @@ class Project < ActiveRecord::Base | ||
| 300 | end | 312 | end |
| 301 | end | 313 | end |
| 302 | 314 | ||
| 303 | - def discover_default_branch | ||
| 304 | - # Discover the default branch, but only if it hasn't already been set to | ||
| 305 | - # something else | ||
| 306 | - if repository.exists? && default_branch.nil? | ||
| 307 | - update_attributes(default_branch: self.repository.discover_default_branch) | ||
| 308 | - end | ||
| 309 | - end | ||
| 310 | - | ||
| 311 | def update_merge_requests(oldrev, newrev, ref, user) | 315 | def update_merge_requests(oldrev, newrev, ref, user) |
| 312 | return true unless ref =~ /heads/ | 316 | return true unless ref =~ /heads/ |
| 313 | branch_name = ref.gsub("refs/heads/", "") | 317 | branch_name = ref.gsub("refs/heads/", "") |
| @@ -390,7 +394,7 @@ class Project < ActiveRecord::Base | @@ -390,7 +394,7 @@ class Project < ActiveRecord::Base | ||
| 390 | end | 394 | end |
| 391 | 395 | ||
| 392 | def http_url_to_repo | 396 | def http_url_to_repo |
| 393 | - http_url = [Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('') | 397 | + [Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('') |
| 394 | end | 398 | end |
| 395 | 399 | ||
| 396 | # Check if current branch name is marked as protected in the system | 400 | # Check if current branch name is marked as protected in the system |
| @@ -451,4 +455,17 @@ class Project < ActiveRecord::Base | @@ -451,4 +455,17 @@ class Project < ActiveRecord::Base | ||
| 451 | def project_member(user) | 455 | def project_member(user) |
| 452 | users_projects.where(user_id: user).first | 456 | users_projects.where(user_id: user).first |
| 453 | end | 457 | end |
| 458 | + | ||
| 459 | + def default_branch | ||
| 460 | + @default_branch ||= repository.root_ref if repository.exists? | ||
| 461 | + end | ||
| 462 | + | ||
| 463 | + def reload_default_branch | ||
| 464 | + @default_branch = nil | ||
| 465 | + default_branch | ||
| 466 | + end | ||
| 467 | + | ||
| 468 | + def visibility_level_field | ||
| 469 | + visibility_level | ||
| 470 | + end | ||
| 454 | end | 471 | end |
app/models/project_hook.rb
| @@ -2,15 +2,24 @@ | @@ -2,15 +2,24 @@ | ||
| 2 | # | 2 | # |
| 3 | # Table name: web_hooks | 3 | # Table name: web_hooks |
| 4 | # | 4 | # |
| 5 | -# id :integer not null, primary key | ||
| 6 | -# url :string(255) | ||
| 7 | -# project_id :integer | ||
| 8 | -# created_at :datetime not null | ||
| 9 | -# updated_at :datetime not null | ||
| 10 | -# type :string(255) default("ProjectHook") | ||
| 11 | -# service_id :integer | 5 | +# id :integer not null, primary key |
| 6 | +# url :string(255) | ||
| 7 | +# project_id :integer | ||
| 8 | +# created_at :datetime not null | ||
| 9 | +# updated_at :datetime not null | ||
| 10 | +# type :string(255) default("ProjectHook") | ||
| 11 | +# service_id :integer | ||
| 12 | +# push_events :boolean default(TRUE), not null | ||
| 13 | +# issues_events :boolean default(FALSE), not null | ||
| 14 | +# merge_requests_events :boolean default(FALSE), not null | ||
| 12 | # | 15 | # |
| 13 | 16 | ||
| 14 | class ProjectHook < WebHook | 17 | class ProjectHook < WebHook |
| 15 | belongs_to :project | 18 | belongs_to :project |
| 19 | + | ||
| 20 | + attr_accessible :push_events, :issues_events, :merge_requests_events | ||
| 21 | + | ||
| 22 | + scope :push_hooks, -> { where(push_events: true) } | ||
| 23 | + scope :issue_hooks, -> { where(issues_events: true) } | ||
| 24 | + scope :merge_request_hooks, -> { where(merge_requests_events: true) } | ||
| 16 | end | 25 | end |
app/models/repository.rb
| @@ -3,7 +3,7 @@ class Repository | @@ -3,7 +3,7 @@ class Repository | ||
| 3 | 3 | ||
| 4 | attr_accessor :raw_repository, :path_with_namespace | 4 | attr_accessor :raw_repository, :path_with_namespace |
| 5 | 5 | ||
| 6 | - def initialize(path_with_namespace, default_branch) | 6 | + def initialize(path_with_namespace, default_branch = nil) |
| 7 | @path_with_namespace = path_with_namespace | 7 | @path_with_namespace = path_with_namespace |
| 8 | @raw_repository = Gitlab::Git::Repository.new(path_to_repo) if path_with_namespace | 8 | @raw_repository = Gitlab::Git::Repository.new(path_to_repo) if path_with_namespace |
| 9 | rescue Gitlab::Git::Repository::NoRepository | 9 | rescue Gitlab::Git::Repository::NoRepository |
| @@ -57,7 +57,7 @@ class Repository | @@ -57,7 +57,7 @@ class Repository | ||
| 57 | 57 | ||
| 58 | def recent_branches(limit = 20) | 58 | def recent_branches(limit = 20) |
| 59 | branches.sort do |a, b| | 59 | branches.sort do |a, b| |
| 60 | - a.commit.committed_date <=> b.commit.committed_date | 60 | + b.commit.committed_date <=> a.commit.committed_date |
| 61 | end[0..limit] | 61 | end[0..limit] |
| 62 | end | 62 | end |
| 63 | 63 | ||
| @@ -133,6 +133,7 @@ class Repository | @@ -133,6 +133,7 @@ class Repository | ||
| 133 | Rails.cache.delete(cache_key(:tag_names)) | 133 | Rails.cache.delete(cache_key(:tag_names)) |
| 134 | Rails.cache.delete(cache_key(:commit_count)) | 134 | Rails.cache.delete(cache_key(:commit_count)) |
| 135 | Rails.cache.delete(cache_key(:graph_log)) | 135 | Rails.cache.delete(cache_key(:graph_log)) |
| 136 | + Rails.cache.delete(cache_key(:readme)) | ||
| 136 | end | 137 | end |
| 137 | 138 | ||
| 138 | def graph_log | 139 | def graph_log |
| @@ -159,4 +160,10 @@ class Repository | @@ -159,4 +160,10 @@ class Repository | ||
| 159 | def blob_at(sha, path) | 160 | def blob_at(sha, path) |
| 160 | Gitlab::Git::Blob.find(self, sha, path) | 161 | Gitlab::Git::Blob.find(self, sha, path) |
| 161 | end | 162 | end |
| 163 | + | ||
| 164 | + def readme | ||
| 165 | + Rails.cache.fetch(cache_key(:readme)) do | ||
| 166 | + Tree.new(self, self.root_ref).readme | ||
| 167 | + end | ||
| 168 | + end | ||
| 162 | end | 169 | end |
app/models/service_hook.rb
| @@ -2,13 +2,16 @@ | @@ -2,13 +2,16 @@ | ||
| 2 | # | 2 | # |
| 3 | # Table name: web_hooks | 3 | # Table name: web_hooks |
| 4 | # | 4 | # |
| 5 | -# id :integer not null, primary key | ||
| 6 | -# url :string(255) | ||
| 7 | -# project_id :integer | ||
| 8 | -# created_at :datetime not null | ||
| 9 | -# updated_at :datetime not null | ||
| 10 | -# type :string(255) default("ProjectHook") | ||
| 11 | -# service_id :integer | 5 | +# id :integer not null, primary key |
| 6 | +# url :string(255) | ||
| 7 | +# project_id :integer | ||
| 8 | +# created_at :datetime not null | ||
| 9 | +# updated_at :datetime not null | ||
| 10 | +# type :string(255) default("ProjectHook") | ||
| 11 | +# service_id :integer | ||
| 12 | +# push_events :boolean default(TRUE), not null | ||
| 13 | +# issues_events :boolean default(FALSE), not null | ||
| 14 | +# merge_requests_events :boolean default(FALSE), not null | ||
| 12 | # | 15 | # |
| 13 | 16 | ||
| 14 | class ServiceHook < WebHook | 17 | class ServiceHook < WebHook |
app/models/system_hook.rb
| @@ -2,13 +2,16 @@ | @@ -2,13 +2,16 @@ | ||
| 2 | # | 2 | # |
| 3 | # Table name: web_hooks | 3 | # Table name: web_hooks |
| 4 | # | 4 | # |
| 5 | -# id :integer not null, primary key | ||
| 6 | -# url :string(255) | ||
| 7 | -# project_id :integer | ||
| 8 | -# created_at :datetime not null | ||
| 9 | -# updated_at :datetime not null | ||
| 10 | -# type :string(255) default("ProjectHook") | ||
| 11 | -# service_id :integer | 5 | +# id :integer not null, primary key |
| 6 | +# url :string(255) | ||
| 7 | +# project_id :integer | ||
| 8 | +# created_at :datetime not null | ||
| 9 | +# updated_at :datetime not null | ||
| 10 | +# type :string(255) default("ProjectHook") | ||
| 11 | +# service_id :integer | ||
| 12 | +# push_events :boolean default(TRUE), not null | ||
| 13 | +# issues_events :boolean default(FALSE), not null | ||
| 14 | +# merge_requests_events :boolean default(FALSE), not null | ||
| 12 | # | 15 | # |
| 13 | 16 | ||
| 14 | class SystemHook < WebHook | 17 | class SystemHook < WebHook |
app/models/user.rb
| @@ -36,6 +36,11 @@ | @@ -36,6 +36,11 @@ | ||
| 36 | # notification_level :integer default(1), not null | 36 | # notification_level :integer default(1), not null |
| 37 | # password_expires_at :datetime | 37 | # password_expires_at :datetime |
| 38 | # created_by_id :integer | 38 | # created_by_id :integer |
| 39 | +# avatar :string(255) | ||
| 40 | +# confirmation_token :string(255) | ||
| 41 | +# confirmed_at :datetime | ||
| 42 | +# confirmation_sent_at :datetime | ||
| 43 | +# unconfirmed_email :string(255) | ||
| 39 | # | 44 | # |
| 40 | 45 | ||
| 41 | require 'carrierwave/orm/activerecord' | 46 | require 'carrierwave/orm/activerecord' |
app/models/web_hook.rb
| @@ -2,13 +2,16 @@ | @@ -2,13 +2,16 @@ | ||
| 2 | # | 2 | # |
| 3 | # Table name: web_hooks | 3 | # Table name: web_hooks |
| 4 | # | 4 | # |
| 5 | -# id :integer not null, primary key | ||
| 6 | -# url :string(255) | ||
| 7 | -# project_id :integer | ||
| 8 | -# created_at :datetime not null | ||
| 9 | -# updated_at :datetime not null | ||
| 10 | -# type :string(255) default("ProjectHook") | ||
| 11 | -# service_id :integer | 5 | +# id :integer not null, primary key |
| 6 | +# url :string(255) | ||
| 7 | +# project_id :integer | ||
| 8 | +# created_at :datetime not null | ||
| 9 | +# updated_at :datetime not null | ||
| 10 | +# type :string(255) default("ProjectHook") | ||
| 11 | +# service_id :integer | ||
| 12 | +# push_events :boolean default(TRUE), not null | ||
| 13 | +# issues_events :boolean default(FALSE), not null | ||
| 14 | +# merge_requests_events :boolean default(FALSE), not null | ||
| 12 | # | 15 | # |
| 13 | 16 | ||
| 14 | class WebHook < ActiveRecord::Base | 17 | class WebHook < ActiveRecord::Base |
app/observers/issue_observer.rb
| 1 | class IssueObserver < BaseObserver | 1 | class IssueObserver < BaseObserver |
| 2 | def after_create(issue) | 2 | def after_create(issue) |
| 3 | notification.new_issue(issue, current_user) | 3 | notification.new_issue(issue, current_user) |
| 4 | - | ||
| 5 | issue.create_cross_references!(issue.project, current_user) | 4 | issue.create_cross_references!(issue.project, current_user) |
| 5 | + execute_hooks(issue) | ||
| 6 | end | 6 | end |
| 7 | 7 | ||
| 8 | def after_close(issue, transition) | 8 | def after_close(issue, transition) |
| 9 | notification.close_issue(issue, current_user) | 9 | notification.close_issue(issue, current_user) |
| 10 | - | ||
| 11 | create_note(issue) | 10 | create_note(issue) |
| 11 | + execute_hooks(issue) | ||
| 12 | end | 12 | end |
| 13 | 13 | ||
| 14 | def after_reopen(issue, transition) | 14 | def after_reopen(issue, transition) |
| @@ -29,4 +29,8 @@ class IssueObserver < BaseObserver | @@ -29,4 +29,8 @@ class IssueObserver < BaseObserver | ||
| 29 | def create_note(issue) | 29 | def create_note(issue) |
| 30 | Note.create_status_change_note(issue, issue.project, current_user, issue.state, current_commit) | 30 | Note.create_status_change_note(issue, issue.project, current_user, issue.state, current_commit) |
| 31 | end | 31 | end |
| 32 | + | ||
| 33 | + def execute_hooks(issue) | ||
| 34 | + issue.project.execute_hooks(issue.to_hook_data, :issue_hooks) | ||
| 35 | + end | ||
| 32 | end | 36 | end |
app/observers/merge_request_observer.rb
| @@ -7,15 +7,15 @@ class MergeRequestObserver < ActivityObserver | @@ -7,15 +7,15 @@ class MergeRequestObserver < ActivityObserver | ||
| 7 | end | 7 | end |
| 8 | 8 | ||
| 9 | notification.new_merge_request(merge_request, current_user) | 9 | notification.new_merge_request(merge_request, current_user) |
| 10 | - | ||
| 11 | merge_request.create_cross_references!(merge_request.project, current_user) | 10 | merge_request.create_cross_references!(merge_request.project, current_user) |
| 11 | + execute_hooks(merge_request) | ||
| 12 | end | 12 | end |
| 13 | 13 | ||
| 14 | def after_close(merge_request, transition) | 14 | def after_close(merge_request, transition) |
| 15 | create_event(merge_request, Event::CLOSED) | 15 | create_event(merge_request, Event::CLOSED) |
| 16 | - Note.create_status_change_note(merge_request, merge_request.target_project, current_user, merge_request.state, nil) | ||
| 17 | - | ||
| 18 | notification.close_mr(merge_request, current_user) | 16 | notification.close_mr(merge_request, current_user) |
| 17 | + create_note(merge_request) | ||
| 18 | + execute_hooks(merge_request) | ||
| 19 | end | 19 | end |
| 20 | 20 | ||
| 21 | def after_merge(merge_request, transition) | 21 | def after_merge(merge_request, transition) |
| @@ -31,11 +31,13 @@ class MergeRequestObserver < ActivityObserver | @@ -31,11 +31,13 @@ class MergeRequestObserver < ActivityObserver | ||
| 31 | action: Event::MERGED, | 31 | action: Event::MERGED, |
| 32 | author_id: merge_request.author_id_of_changes | 32 | author_id: merge_request.author_id_of_changes |
| 33 | ) | 33 | ) |
| 34 | + | ||
| 35 | + execute_hooks(merge_request) | ||
| 34 | end | 36 | end |
| 35 | 37 | ||
| 36 | def after_reopen(merge_request, transition) | 38 | def after_reopen(merge_request, transition) |
| 37 | create_event(merge_request, Event::REOPENED) | 39 | create_event(merge_request, Event::REOPENED) |
| 38 | - Note.create_status_change_note(merge_request, merge_request.target_project, current_user, merge_request.state, nil) | 40 | + create_note(merge_request) |
| 39 | end | 41 | end |
| 40 | 42 | ||
| 41 | def after_update(merge_request) | 43 | def after_update(merge_request) |
| @@ -53,4 +55,17 @@ class MergeRequestObserver < ActivityObserver | @@ -53,4 +55,17 @@ class MergeRequestObserver < ActivityObserver | ||
| 53 | author_id: current_user.id | 55 | author_id: current_user.id |
| 54 | ) | 56 | ) |
| 55 | end | 57 | end |
| 58 | + | ||
| 59 | + private | ||
| 60 | + | ||
| 61 | + # Create merge request note with service comment like 'Status changed to closed' | ||
| 62 | + def create_note(merge_request) | ||
| 63 | + Note.create_status_change_note(merge_request, merge_request.target_project, current_user, merge_request.state, nil) | ||
| 64 | + end | ||
| 65 | + | ||
| 66 | + def execute_hooks(merge_request) | ||
| 67 | + if merge_request.project | ||
| 68 | + merge_request.project.execute_hooks(merge_request.to_hook_data, :merge_request_hooks) | ||
| 69 | + end | ||
| 70 | + end | ||
| 56 | end | 71 | end |
app/observers/project_observer.rb
| @@ -30,12 +30,6 @@ class ProjectObserver < BaseObserver | @@ -30,12 +30,6 @@ class ProjectObserver < BaseObserver | ||
| 30 | def after_update(project) | 30 | def after_update(project) |
| 31 | project.send_move_instructions if project.namespace_id_changed? | 31 | project.send_move_instructions if project.namespace_id_changed? |
| 32 | project.rename_repo if project.path_changed? | 32 | project.rename_repo if project.path_changed? |
| 33 | - | ||
| 34 | - GitlabShellWorker.perform_async( | ||
| 35 | - :update_repository_head, | ||
| 36 | - project.path_with_namespace, | ||
| 37 | - project.default_branch | ||
| 38 | - ) if project.default_branch_changed? | ||
| 39 | end | 33 | end |
| 40 | 34 | ||
| 41 | def before_destroy(project) | 35 | def before_destroy(project) |
app/observers/users_group_observer.rb
| @@ -4,6 +4,6 @@ class UsersGroupObserver < BaseObserver | @@ -4,6 +4,6 @@ class UsersGroupObserver < BaseObserver | ||
| 4 | end | 4 | end |
| 5 | 5 | ||
| 6 | def after_update(membership) | 6 | def after_update(membership) |
| 7 | - notification.update_group_member(membership) | 7 | + notification.update_group_member(membership) if membership.group_access_changed? |
| 8 | end | 8 | end |
| 9 | end | 9 | end |
app/services/git_push_service.rb
| @@ -24,7 +24,6 @@ class GitPushService | @@ -24,7 +24,6 @@ class GitPushService | ||
| 24 | create_push_event | 24 | create_push_event |
| 25 | 25 | ||
| 26 | project.ensure_satellite_exists | 26 | project.ensure_satellite_exists |
| 27 | - project.discover_default_branch | ||
| 28 | project.repository.expire_cache | 27 | project.repository.expire_cache |
| 29 | 28 | ||
| 30 | if push_to_existing_branch?(ref, oldrev) | 29 | if push_to_existing_branch?(ref, oldrev) |
| @@ -33,7 +32,7 @@ class GitPushService | @@ -33,7 +32,7 @@ class GitPushService | ||
| 33 | end | 32 | end |
| 34 | 33 | ||
| 35 | if push_to_branch?(ref) | 34 | if push_to_branch?(ref) |
| 36 | - project.execute_hooks(@push_data.dup) | 35 | + project.execute_hooks(@push_data.dup, :push_hooks) |
| 37 | project.execute_services(@push_data.dup) | 36 | project.execute_services(@push_data.dup) |
| 38 | end | 37 | end |
| 39 | 38 |
app/services/notification_service.rb
| @@ -19,7 +19,7 @@ class NotificationService | @@ -19,7 +19,7 @@ class NotificationService | ||
| 19 | 19 | ||
| 20 | # When create an issue we should send next emails: | 20 | # When create an issue we should send next emails: |
| 21 | # | 21 | # |
| 22 | - # * issue assignee if his notification level is not Disabled | 22 | + # * issue assignee if their notification level is not Disabled |
| 23 | # * project team members with notification level higher then Participating | 23 | # * project team members with notification level higher then Participating |
| 24 | # | 24 | # |
| 25 | def new_issue(issue, current_user) | 25 | def new_issue(issue, current_user) |
| @@ -28,8 +28,8 @@ class NotificationService | @@ -28,8 +28,8 @@ class NotificationService | ||
| 28 | 28 | ||
| 29 | # When we close an issue we should send next emails: | 29 | # When we close an issue we should send next emails: |
| 30 | # | 30 | # |
| 31 | - # * issue author if his notification level is not Disabled | ||
| 32 | - # * issue assignee if his notification level is not Disabled | 31 | + # * issue author if their notification level is not Disabled |
| 32 | + # * issue assignee if their notification level is not Disabled | ||
| 33 | # * project team members with notification level higher then Participating | 33 | # * project team members with notification level higher then Participating |
| 34 | # | 34 | # |
| 35 | def close_issue(issue, current_user) | 35 | def close_issue(issue, current_user) |
| @@ -38,8 +38,8 @@ class NotificationService | @@ -38,8 +38,8 @@ class NotificationService | ||
| 38 | 38 | ||
| 39 | # When we reassign an issue we should send next emails: | 39 | # When we reassign an issue we should send next emails: |
| 40 | # | 40 | # |
| 41 | - # * issue old assignee if his notification level is not Disabled | ||
| 42 | - # * issue new assignee if his notification level is not Disabled | 41 | + # * issue old assignee if their notification level is not Disabled |
| 42 | + # * issue new assignee if their notification level is not Disabled | ||
| 43 | # | 43 | # |
| 44 | def reassigned_issue(issue, current_user) | 44 | def reassigned_issue(issue, current_user) |
| 45 | reassign_resource_email(issue, issue.project, current_user, 'reassigned_issue_email') | 45 | reassign_resource_email(issue, issue.project, current_user, 'reassigned_issue_email') |
| @@ -48,7 +48,7 @@ class NotificationService | @@ -48,7 +48,7 @@ class NotificationService | ||
| 48 | 48 | ||
| 49 | # When create a merge request we should send next emails: | 49 | # When create a merge request we should send next emails: |
| 50 | # | 50 | # |
| 51 | - # * mr assignee if his notification level is not Disabled | 51 | + # * mr assignee if their notification level is not Disabled |
| 52 | # | 52 | # |
| 53 | def new_merge_request(merge_request, current_user) | 53 | def new_merge_request(merge_request, current_user) |
| 54 | new_resource_email(merge_request, merge_request.target_project, 'new_merge_request_email') | 54 | new_resource_email(merge_request, merge_request.target_project, 'new_merge_request_email') |
| @@ -56,8 +56,8 @@ class NotificationService | @@ -56,8 +56,8 @@ class NotificationService | ||
| 56 | 56 | ||
| 57 | # When we reassign a merge_request we should send next emails: | 57 | # When we reassign a merge_request we should send next emails: |
| 58 | # | 58 | # |
| 59 | - # * merge_request old assignee if his notification level is not Disabled | ||
| 60 | - # * merge_request assignee if his notification level is not Disabled | 59 | + # * merge_request old assignee if their notification level is not Disabled |
| 60 | + # * merge_request assignee if their notification level is not Disabled | ||
| 61 | # | 61 | # |
| 62 | def reassigned_merge_request(merge_request, current_user) | 62 | def reassigned_merge_request(merge_request, current_user) |
| 63 | reassign_resource_email(merge_request, merge_request.target_project, current_user, 'reassigned_merge_request_email') | 63 | reassign_resource_email(merge_request, merge_request.target_project, current_user, 'reassigned_merge_request_email') |
| @@ -65,8 +65,8 @@ class NotificationService | @@ -65,8 +65,8 @@ class NotificationService | ||
| 65 | 65 | ||
| 66 | # When we close a merge request we should send next emails: | 66 | # When we close a merge request we should send next emails: |
| 67 | # | 67 | # |
| 68 | - # * merge_request author if his notification level is not Disabled | ||
| 69 | - # * merge_request assignee if his notification level is not Disabled | 68 | + # * merge_request author if their notification level is not Disabled |
| 69 | + # * merge_request assignee if their notification level is not Disabled | ||
| 70 | # * project team members with notification level higher then Participating | 70 | # * project team members with notification level higher then Participating |
| 71 | # | 71 | # |
| 72 | def close_mr(merge_request, current_user) | 72 | def close_mr(merge_request, current_user) |
| @@ -75,8 +75,8 @@ class NotificationService | @@ -75,8 +75,8 @@ class NotificationService | ||
| 75 | 75 | ||
| 76 | # When we merge a merge request we should send next emails: | 76 | # When we merge a merge request we should send next emails: |
| 77 | # | 77 | # |
| 78 | - # * merge_request author if his notification level is not Disabled | ||
| 79 | - # * merge_request assignee if his notification level is not Disabled | 78 | + # * merge_request author if their notification level is not Disabled |
| 79 | + # * merge_request assignee if their notification level is not Disabled | ||
| 80 | # * project team members with notification level higher then Participating | 80 | # * project team members with notification level higher then Participating |
| 81 | # | 81 | # |
| 82 | def merge_mr(merge_request) | 82 | def merge_mr(merge_request) |
app/services/project_transfer_service.rb
| @@ -18,6 +18,10 @@ class ProjectTransferService | @@ -18,6 +18,10 @@ class ProjectTransferService | ||
| 18 | raise TransferError.new("Project with same path in target namespace already exists") | 18 | raise TransferError.new("Project with same path in target namespace already exists") |
| 19 | end | 19 | end |
| 20 | 20 | ||
| 21 | + # Remove old satellite | ||
| 22 | + project.satellite.destroy | ||
| 23 | + | ||
| 24 | + # Apply new namespace id | ||
| 21 | project.namespace = new_namespace | 25 | project.namespace = new_namespace |
| 22 | project.save! | 26 | project.save! |
| 23 | 27 | ||
| @@ -29,8 +33,8 @@ class ProjectTransferService | @@ -29,8 +33,8 @@ class ProjectTransferService | ||
| 29 | # Move wiki repo also if present | 33 | # Move wiki repo also if present |
| 30 | gitlab_shell.mv_repository("#{old_path}.wiki", "#{new_path}.wiki") | 34 | gitlab_shell.mv_repository("#{old_path}.wiki", "#{new_path}.wiki") |
| 31 | 35 | ||
| 32 | - # create satellite repo | ||
| 33 | - project.ensure_satellite_exists | 36 | + # Create a new satellite (reload project from DB) |
| 37 | + Project.find(project.id).ensure_satellite_exists | ||
| 34 | 38 | ||
| 35 | # clear project cached events | 39 | # clear project cached events |
| 36 | project.reset_events_cache | 40 | project.reset_events_cache |
| @@ -0,0 +1,46 @@ | @@ -0,0 +1,46 @@ | ||
| 1 | +%h3.page-title | ||
| 2 | + Broadcast Messages | ||
| 3 | +%p.light | ||
| 4 | + Broadcast messages are displayed for every user and can be used to notify users about scheduled maintenance, recent upgrades and more. | ||
| 5 | +%hr | ||
| 6 | + | ||
| 7 | += form_for [:admin, @broadcast_message] do |f| | ||
| 8 | + -if @broadcast_message.errors.any? | ||
| 9 | + .alert.alert-error | ||
| 10 | + - @broadcast_message.errors.full_messages.each do |msg| | ||
| 11 | + %p= msg | ||
| 12 | + .control-group | ||
| 13 | + = f.label :message | ||
| 14 | + .controls | ||
| 15 | + = f.text_area :message, class: "input-xxlarge", rows: 2, required: true | ||
| 16 | + .control-group | ||
| 17 | + = f.label :starts_at | ||
| 18 | + .controls.datetime-controls | ||
| 19 | + = f.datetime_select :starts_at | ||
| 20 | + .control-group | ||
| 21 | + = f.label :ends_at | ||
| 22 | + .controls.datetime-controls | ||
| 23 | + = f.datetime_select :ends_at | ||
| 24 | + .form-actions | ||
| 25 | + = f.submit "Add broadcast message", class: "btn btn-create" | ||
| 26 | + | ||
| 27 | +-if @broadcast_messages.any? | ||
| 28 | + %ul.bordered-list.broadcast-messages | ||
| 29 | + - @broadcast_messages.each do |broadcast_message| | ||
| 30 | + %li | ||
| 31 | + .pull-right | ||
| 32 | + - if broadcast_message.starts_at | ||
| 33 | + %strong | ||
| 34 | + #{broadcast_message.starts_at.to_s(:short)} | ||
| 35 | + \... | ||
| 36 | + - if broadcast_message.ends_at | ||
| 37 | + %strong | ||
| 38 | + #{broadcast_message.ends_at.to_s(:short)} | ||
| 39 | + | ||
| 40 | + = link_to [:admin, broadcast_message], method: :delete, remote: true, class: 'remove-row btn btn-tiny' do | ||
| 41 | + %i.icon-remove.cred | ||
| 42 | + | ||
| 43 | + .message= broadcast_message.message | ||
| 44 | + | ||
| 45 | + | ||
| 46 | + = paginate @broadcast_messages |
app/views/admin/dashboard/index.html.haml
| @@ -52,6 +52,19 @@ | @@ -52,6 +52,19 @@ | ||
| 52 | ago | 52 | ago |
| 53 | 53 | ||
| 54 | .span4 | 54 | .span4 |
| 55 | + %h4 Latest groups | ||
| 56 | + %hr | ||
| 57 | + - @groups.each do |group| | ||
| 58 | + %p | ||
| 59 | + = link_to [:admin, group] do | ||
| 60 | + = group.name | ||
| 61 | + %span.light.pull-right | ||
| 62 | + = time_ago_in_words group.created_at | ||
| 63 | + ago | ||
| 64 | + | ||
| 65 | +%br | ||
| 66 | +.row | ||
| 67 | + .span4 | ||
| 55 | %h4 Stats | 68 | %h4 Stats |
| 56 | %hr | 69 | %hr |
| 57 | %p | 70 | %p |
| @@ -82,3 +95,43 @@ | @@ -82,3 +95,43 @@ | ||
| 82 | Milestones | 95 | Milestones |
| 83 | %span.light.pull-right | 96 | %span.light.pull-right |
| 84 | = Milestone.count | 97 | = Milestone.count |
| 98 | + .span4 | ||
| 99 | + %h4 | ||
| 100 | + Features | ||
| 101 | + %hr | ||
| 102 | + %p | ||
| 103 | + Sign up | ||
| 104 | + %span.light.pull-right | ||
| 105 | + = boolean_to_icon gitlab_config.signup_enabled | ||
| 106 | + %p | ||
| 107 | + LDAP | ||
| 108 | + %span.light.pull-right | ||
| 109 | + = boolean_to_icon Gitlab.config.ldap.enabled | ||
| 110 | + %p | ||
| 111 | + Gravatar | ||
| 112 | + %span.light.pull-right | ||
| 113 | + = boolean_to_icon Gitlab.config.gravatar.enabled | ||
| 114 | + %p | ||
| 115 | + OmniAuth | ||
| 116 | + %span.light.pull-right | ||
| 117 | + = boolean_to_icon Gitlab.config.omniauth.enabled | ||
| 118 | + .span4 | ||
| 119 | + %h4 Components | ||
| 120 | + %hr | ||
| 121 | + %p | ||
| 122 | + GitLab | ||
| 123 | + %span.pull-right | ||
| 124 | + = Gitlab::VERSION | ||
| 125 | + %p | ||
| 126 | + GitLab Shell | ||
| 127 | + %span.pull-right | ||
| 128 | + = Gitlab::Shell.new.version | ||
| 129 | + %p | ||
| 130 | + Ruby | ||
| 131 | + %span.pull-right | ||
| 132 | + #{RUBY_VERSION}p#{RUBY_PATCHLEVEL} | ||
| 133 | + | ||
| 134 | + %p | ||
| 135 | + Rails | ||
| 136 | + %span.pull-right | ||
| 137 | + #{Rails::VERSION::STRING} |
app/views/admin/groups/index.html.haml
| @@ -7,7 +7,7 @@ | @@ -7,7 +7,7 @@ | ||
| 7 | = link_to 'New Group', new_admin_group_path, class: "btn btn-new pull-right" | 7 | = link_to 'New Group', new_admin_group_path, class: "btn btn-new pull-right" |
| 8 | %br | 8 | %br |
| 9 | = form_tag admin_groups_path, method: :get, class: 'form-inline' do | 9 | = form_tag admin_groups_path, method: :get, class: 'form-inline' do |
| 10 | - = text_field_tag :name, params[:name], class: "span6" | 10 | + = text_field_tag :name, params[:name], class: "span6 input-xpadding" |
| 11 | = submit_tag "Search", class: "btn submit btn-primary" | 11 | = submit_tag "Search", class: "btn submit btn-primary" |
| 12 | 12 | ||
| 13 | %hr | 13 | %hr |
app/views/admin/groups/show.html.haml
| @@ -39,6 +39,8 @@ | @@ -39,6 +39,8 @@ | ||
| 39 | %li | 39 | %li |
| 40 | %strong | 40 | %strong |
| 41 | = link_to project.name_with_namespace, [:admin, project] | 41 | = link_to project.name_with_namespace, [:admin, project] |
| 42 | + %span.label.label-gray | ||
| 43 | + = repository_size(project) | ||
| 42 | %span.pull-right.light | 44 | %span.pull-right.light |
| 43 | %span.monospace= project.path_with_namespace + ".git" | 45 | %span.monospace= project.path_with_namespace + ".git" |
| 44 | 46 |
app/views/admin/projects/index.html.haml
| @@ -10,11 +10,15 @@ | @@ -10,11 +10,15 @@ | ||
| 10 | .control-group | 10 | .control-group |
| 11 | = label_tag :owner_id, 'Owner:', class: 'control-label' | 11 | = label_tag :owner_id, 'Owner:', class: 'control-label' |
| 12 | .controls | 12 | .controls |
| 13 | - = users_select_tag :owner_id, selected: params[:owner_id], class: 'input-large' | ||
| 14 | - .control-group | ||
| 15 | - = label_tag :public_only, 'Public Only', class: 'control-label' | ||
| 16 | - .controls | ||
| 17 | - = check_box_tag :public_only, 1, params[:public_only] | 13 | + = users_select_tag :owner_id, selected: params[:owner_id], class: 'input-large input-clamp' |
| 14 | + .control-group.visibility-levels | ||
| 15 | + = label_tag :visibility_level, 'Visibility Levels', class: 'control-label' | ||
| 16 | + - Project.visibility_levels.each do |label, level| | ||
| 17 | + .controls | ||
| 18 | + = check_box_tag 'visibility_levels[]', level, params[:visibility_levels].present? && params[:visibility_levels].include?(level.to_s) | ||
| 19 | + %span.descr | ||
| 20 | + = visibility_level_icon(level) | ||
| 21 | + = label | ||
| 18 | .control-group | 22 | .control-group |
| 19 | = label_tag :with_push, 'Not empty', class: 'control-label' | 23 | = label_tag :with_push, 'Not empty', class: 'control-label' |
| 20 | .controls | 24 | .controls |
| @@ -42,12 +46,12 @@ | @@ -42,12 +46,12 @@ | ||
| 42 | %ul.well-list | 46 | %ul.well-list |
| 43 | - @projects.each do |project| | 47 | - @projects.each do |project| |
| 44 | %li | 48 | %li |
| 45 | - - if project.public | ||
| 46 | - = public_icon | ||
| 47 | - - else | ||
| 48 | - = private_icon | 49 | + %span{ class: visibility_level_color(project.visibility_level) } |
| 50 | + = visibility_level_icon(project.visibility_level) | ||
| 49 | = link_to project.name_with_namespace, [:admin, project] | 51 | = link_to project.name_with_namespace, [:admin, project] |
| 50 | .pull-right | 52 | .pull-right |
| 53 | + %span.label.label-gray | ||
| 54 | + = repository_size(project) | ||
| 51 | = link_to 'Edit', edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small" | 55 | = link_to 'Edit', edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small" |
| 52 | = link_to 'Destroy', [project], confirm: remove_project_message(project), method: :delete, class: "btn btn-small btn-remove" | 56 | = link_to 'Destroy', [project], confirm: remove_project_message(project), method: :delete, class: "btn btn-small btn-remove" |
| 53 | - if @projects.blank? | 57 | - if @projects.blank? |
app/views/admin/projects/show.html.haml
| @@ -66,14 +66,24 @@ | @@ -66,14 +66,24 @@ | ||
| 66 | %li | 66 | %li |
| 67 | %span.light access: | 67 | %span.light access: |
| 68 | %strong | 68 | %strong |
| 69 | - - if @project.public | ||
| 70 | - %span.cblue | ||
| 71 | - %i.icon-share | ||
| 72 | - Public | ||
| 73 | - - else | ||
| 74 | - %span.cgreen | ||
| 75 | - %i.icon-lock | ||
| 76 | - Private | 69 | + %span{ class: visibility_level_color(@project.visibility_level) } |
| 70 | + = visibility_level_icon(@project.visibility_level) | ||
| 71 | + = visibility_level_label(@project.visibility_level) | ||
| 72 | + | ||
| 73 | + .ui-box | ||
| 74 | + .title | ||
| 75 | + Transfer project | ||
| 76 | + .ui-box-body | ||
| 77 | + = form_for @project, url: transfer_admin_project_path(@project), method: :put do |f| | ||
| 78 | + .control-group | ||
| 79 | + = f.label :namespace_id, "Namespace" | ||
| 80 | + .controls | ||
| 81 | + = namespace_select_tag :namespace_id, selected: params[:namespace_id], class: 'input-large' | ||
| 82 | + | ||
| 83 | + .control-group | ||
| 84 | + .controls | ||
| 85 | + = f.submit 'Transfer', class: 'btn btn-primary' | ||
| 86 | + | ||
| 77 | .span6 | 87 | .span6 |
| 78 | - if @group | 88 | - if @group |
| 79 | .ui-box | 89 | .ui-box |
app/views/admin/users/index.html.haml
| @@ -2,8 +2,8 @@ | @@ -2,8 +2,8 @@ | ||
| 2 | .span3 | 2 | .span3 |
| 3 | .admin-filter | 3 | .admin-filter |
| 4 | = form_tag admin_users_path, method: :get, class: 'form-inline' do | 4 | = form_tag admin_users_path, method: :get, class: 'form-inline' do |
| 5 | - = search_field_tag :name, params[:name], placeholder: 'Name, email or username', class: 'search-text-input span2' | ||
| 6 | - = button_tag type: 'submit', class: 'btn' do | 5 | + = search_field_tag :name, params[:name], placeholder: 'Name, email or username', class: 'input-xpadding span2' |
| 6 | + = button_tag type: 'submit', class: 'btn btn-primary' do | ||
| 7 | %i.icon-search | 7 | %i.icon-search |
| 8 | %ul.nav.nav-pills.nav-stacked | 8 | %ul.nav.nav-pills.nav-stacked |
| 9 | %li{class: "#{'active' unless params[:filter]}"} | 9 | %li{class: "#{'active' unless params[:filter]}"} |
app/views/dashboard/projects.html.haml
| @@ -26,6 +26,14 @@ | @@ -26,6 +26,14 @@ | ||
| 26 | %span.pull-right | 26 | %span.pull-right |
| 27 | = current_user.owned_projects.count | 27 | = current_user.owned_projects.count |
| 28 | 28 | ||
| 29 | + %fieldset | ||
| 30 | + %legend Visibility | ||
| 31 | + %ul.bordered-list.visibility-filter | ||
| 32 | + - Gitlab::VisibilityLevel.values.each do |level| | ||
| 33 | + %li{ class: (level.to_s == params[:visibility_level]) ? 'active' : 'light' } | ||
| 34 | + = link_to projects_dashboard_path(visibility_level: level) do | ||
| 35 | + = visibility_level_icon(level) | ||
| 36 | + = visibility_level_label(level) | ||
| 29 | 37 | ||
| 30 | - if @groups.present? | 38 | - if @groups.present? |
| 31 | %fieldset | 39 | %fieldset |
| @@ -56,12 +64,10 @@ | @@ -56,12 +64,10 @@ | ||
| 56 | - @projects.each do |project| | 64 | - @projects.each do |project| |
| 57 | %li.my-project-row | 65 | %li.my-project-row |
| 58 | %h4.project-title | 66 | %h4.project-title |
| 67 | + .project-access-icon | ||
| 68 | + = visibility_level_icon(project.visibility_level) | ||
| 59 | = link_to project_path(project), class: dom_class(project) do | 69 | = link_to project_path(project), class: dom_class(project) do |
| 60 | = project.name_with_namespace | 70 | = project.name_with_namespace |
| 61 | - - if project.public | ||
| 62 | - %small.access-icon | ||
| 63 | - = public_icon | ||
| 64 | - Public | ||
| 65 | 71 | ||
| 66 | - if current_user.can_leave_project?(project) | 72 | - if current_user.can_leave_project?(project) |
| 67 | .pull-right | 73 | .pull-right |
app/views/dashboard/show.js.haml
app/views/devise/confirmations/new.html.erb
| @@ -1,12 +0,0 @@ | @@ -1,12 +0,0 @@ | ||
| 1 | -<h2>Resend confirmation instructions</h2> | ||
| 2 | - | ||
| 3 | -<%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %> | ||
| 4 | - <%= devise_error_messages! %> | ||
| 5 | - | ||
| 6 | - <div><%= f.label :email %><br /> | ||
| 7 | - <%= f.email_field :email %></div> | ||
| 8 | - | ||
| 9 | - <div><%= f.submit "Resend confirmation instructions" %></div> | ||
| 10 | -<% end %> | ||
| 11 | - | ||
| 12 | -<%= render partial: "devise/shared/links" %> |
| @@ -0,0 +1,8 @@ | @@ -0,0 +1,8 @@ | ||
| 1 | +.login-box | ||
| 2 | + %h3.page-title Resend confirmation instructions | ||
| 3 | + = form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| | ||
| 4 | + = devise_error_messages! | ||
| 5 | + = f.email_field :email, placeholder: 'Email' | ||
| 6 | + %div= f.submit "Resend confirmation instructions", class: 'btn btn-success' | ||
| 7 | + %hr | ||
| 8 | + = link_to "Sign in", new_session_path(resource_name) |
| @@ -0,0 +1 @@ | @@ -0,0 +1 @@ | ||
| 1 | += render @events |
app/views/groups/edit.html.haml
| @@ -51,10 +51,7 @@ | @@ -51,10 +51,7 @@ | ||
| 51 | %ul.well-list | 51 | %ul.well-list |
| 52 | - @group.projects.each do |project| | 52 | - @group.projects.each do |project| |
| 53 | %li | 53 | %li |
| 54 | - - if project.public | ||
| 55 | - = public_icon | ||
| 56 | - - else | ||
| 57 | - = private_icon | 54 | + = visibility_level_icon(project.visibility_level) |
| 58 | = link_to project.name_with_namespace, project | 55 | = link_to project.name_with_namespace, project |
| 59 | .pull-right | 56 | .pull-right |
| 60 | = link_to 'Members', project_team_index_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small" | 57 | = link_to 'Members', project_team_index_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small" |
app/views/groups/show.js.haml
app/views/help/_layout.html.haml
| 1 | .row | 1 | .row |
| 2 | .span3{:"data-spy" => 'affix'} | 2 | .span3{:"data-spy" => 'affix'} |
| 3 | - .ui-box | ||
| 4 | - .title | ||
| 5 | - Help | ||
| 6 | - %ul.well-list | ||
| 7 | - %li | ||
| 8 | - %strong= link_to "Workflow", help_workflow_path | ||
| 9 | - %li | ||
| 10 | - %strong= link_to "SSH keys", help_ssh_path | ||
| 11 | - | ||
| 12 | - %li | ||
| 13 | - %strong= link_to "GitLab Markdown", help_markdown_path | ||
| 14 | - | ||
| 15 | - %li | ||
| 16 | - %strong= link_to "Permissions", help_permissions_path | ||
| 17 | - | ||
| 18 | - %li | ||
| 19 | - %strong= link_to "API", help_api_path | ||
| 20 | - | ||
| 21 | - %li | ||
| 22 | - %strong= link_to "Web Hooks", help_web_hooks_path | ||
| 23 | - | ||
| 24 | - %li | ||
| 25 | - %strong= link_to "Rake Tasks", help_raketasks_path | ||
| 26 | - | ||
| 27 | - %li | ||
| 28 | - %strong= link_to "System Hooks", help_system_hooks_path | ||
| 29 | - | ||
| 30 | - %li | ||
| 31 | - %strong= link_to "Public Access", help_public_access_path | ||
| 32 | - | ||
| 33 | - %li | ||
| 34 | - %strong= link_to "Security", help_security_path | 3 | + %h3.page-title Help |
| 4 | + %ul.nav.nav-pills.nav-stacked | ||
| 5 | + - links = {:"Workflow" => help_workflow_path, :"SSH Keys" => help_ssh_path, :"GitLab Markdown" => help_markdown_path, :"Permissions" => help_permissions_path, :"API" => help_api_path, :"Web Hooks" => help_web_hooks_path, :"Rake Tasks" => help_raketasks_path, :"System Hooks" => help_system_hooks_path, :"Public Access" => help_public_access_path, :"Security" => help_security_path} | ||
| 6 | + - links.each do |title,path| | ||
| 7 | + %li{class: current_page?(path) ? 'active' : nil} | ||
| 8 | + = link_to title, path | ||
| 35 | 9 | ||
| 36 | .span9.pull-right | 10 | .span9.pull-right |
| 37 | = yield | 11 | = yield |
app/views/help/permissions.html.haml
app/views/help/public_access.html.haml
| 1 | = render layout: 'help/layout' do | 1 | = render layout: 'help/layout' do |
| 2 | %h3.page-title Public Access | 2 | %h3.page-title Public Access |
| 3 | 3 | ||
| 4 | - %p | ||
| 5 | - GitLab allows you to open selected projects to be accessed publicly. | ||
| 6 | - These projects will be cloneable | ||
| 7 | - %em without any | ||
| 8 | - authentication. | ||
| 9 | - Also they will be listed on the #{link_to "public access directory", public_root_path}. | 4 | + %p.slead |
| 5 | + GitLab allows you to open selected projects to be accessed | ||
| 6 | + %strong publicly | ||
| 7 | + or | ||
| 8 | + %strong internally | ||
| 9 | + \. | ||
| 10 | + %br | ||
| 11 | + Projects with either of these visibility levels will be listed in the #{link_to "public access directory", public_root_path}. | ||
| 12 | + %br | ||
| 13 | + Internal projects will only be available to authenticated users. | ||
| 10 | 14 | ||
| 15 | + .clearfix | ||
| 16 | + .dashboard-intro-icon | ||
| 17 | + = public_icon | ||
| 18 | + %h4 | ||
| 19 | + Public projects | ||
| 20 | + %p | ||
| 21 | + Public project can be cloned | ||
| 22 | + %strong without any | ||
| 23 | + authentication. | ||
| 24 | + %br | ||
| 25 | + It will also be listed on the #{link_to "public access directory", public_root_path}. | ||
| 26 | + %br | ||
| 27 | + %strong Any logged in user | ||
| 28 | + will have #{link_to "Guest", help_permissions_path} permissions on the repository. | ||
| 29 | + | ||
| 30 | + .clearfix | ||
| 31 | + .dashboard-intro-icon | ||
| 32 | + = internal_icon | ||
| 33 | + %h4 | ||
| 34 | + Internal projects | ||
| 35 | + %p | ||
| 36 | + Internal project can be cloned by any logged in user. | ||
| 37 | + %br | ||
| 38 | + It will also be listed on the #{link_to "public access directory", public_root_path} for logged in users. | ||
| 39 | + %br | ||
| 40 | + Any logged in user will have #{link_to "Guest", help_permissions_path} permissions on the repository. | ||
| 41 | + | ||
| 42 | + %h4 How to change project visibility | ||
| 11 | %ol | 43 | %ol |
| 12 | %li Go to your project dashboard | 44 | %li Go to your project dashboard |
| 13 | %li Click on the "Edit" tab | 45 | %li Click on the "Edit" tab |
| 14 | - %li Select "Public clone access" | ||
| 15 | - | 46 | + %li Change "Visibility Level" |
app/views/help/web_hooks.html.haml
| 1 | = render layout: 'help/layout' do | 1 | = render layout: 'help/layout' do |
| 2 | - %h3.page-title Web hooks | 2 | + %h3.page-title Project web hooks |
| 3 | + %p.light | ||
| 4 | + Project web hooks allow you to trigger url if new code is pushed or new issue is created | ||
| 5 | + %hr | ||
| 3 | 6 | ||
| 4 | %p.slead | 7 | %p.slead |
| 5 | - Every GitLab project can trigger a web server whenever the repo is pushed to. | 8 | + You can configure web hook to listen for specific events like pushes, issues, merge requests. |
| 9 | + %br | ||
| 10 | + GitLab will send POST request with data to web hook url. | ||
| 6 | %br | 11 | %br |
| 7 | Web Hooks can be used to update an external issue tracker, trigger CI builds, update a backup mirror, or even deploy to your production server. | 12 | Web Hooks can be used to update an external issue tracker, trigger CI builds, update a backup mirror, or even deploy to your production server. |
| 13 | + %hr | ||
| 14 | + | ||
| 15 | + %h4 Push events | ||
| 16 | + %p.light | ||
| 17 | + Triggered when you push to the repository except pushing tags. | ||
| 8 | %br | 18 | %br |
| 9 | - GitLab will send POST request with commits information on every push. | ||
| 10 | - %h5 Hooks request example: | ||
| 11 | - = render "projects/hooks/data_ex" | 19 | + Request body: |
| 20 | + = highlight_js do | ||
| 21 | + :erb | ||
| 22 | + { | ||
| 23 | + "before": "95790bf891e76fee5e1747ab589903a6a1f80f22", | ||
| 24 | + "after": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", | ||
| 25 | + "ref": "refs/heads/master", | ||
| 26 | + "user_id": 4, | ||
| 27 | + "user_name": "John Smith", | ||
| 28 | + "project_id": 15, | ||
| 29 | + "repository": { | ||
| 30 | + "name": "Diaspora", | ||
| 31 | + "url": "git@localhost:diaspora.git", | ||
| 32 | + "description": "", | ||
| 33 | + "homepage": "http://localhost/diaspora", | ||
| 34 | + }, | ||
| 35 | + "commits": [ | ||
| 36 | + { | ||
| 37 | + "id": "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", | ||
| 38 | + "message": "Update Catalan translation to e38cb41.", | ||
| 39 | + "timestamp": "2011-12-12T14:27:31+02:00", | ||
| 40 | + "url": "http://localhost/diaspora/commits/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", | ||
| 41 | + "author": { | ||
| 42 | + "name": "Jordi Mallach", | ||
| 43 | + "email": "jordi@softcatala.org", | ||
| 44 | + } | ||
| 45 | + }, | ||
| 46 | + // ... | ||
| 47 | + { | ||
| 48 | + "id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", | ||
| 49 | + "message": "fixed readme", | ||
| 50 | + "timestamp": "2012-01-03T23:36:29+02:00", | ||
| 51 | + "url": "http://localhost/diaspora/commits/da1560886d4f094c3e6c9ef40349f7d38b5d27d7", | ||
| 52 | + "author": { | ||
| 53 | + "name": "GitLab dev user", | ||
| 54 | + "email": "gitlabdev@dv6700.(none)", | ||
| 55 | + }, | ||
| 56 | + }, | ||
| 57 | + ], | ||
| 58 | + "total_commits_count": 4, | ||
| 59 | + }; | ||
| 60 | + | ||
| 12 | 61 | ||
| 62 | + %h4.prepend-top-20 Issues events | ||
| 63 | + %p.light | ||
| 64 | + Triggered when new issue created or existing issue was closed. | ||
| 65 | + %br | ||
| 66 | + Request body: | ||
| 67 | + = highlight_js do | ||
| 68 | + :erb | ||
| 69 | + { | ||
| 70 | + "object_kind":"issue", | ||
| 71 | + "object_attributes":{ | ||
| 72 | + "id":301, | ||
| 73 | + "title":"New API: create/update/delete file", | ||
| 74 | + "assignee_id":51, | ||
| 75 | + "author_id":51, | ||
| 76 | + "project_id":14, | ||
| 77 | + "created_at":"2013-12-03T17:15:43Z", | ||
| 78 | + "updated_at":"2013-12-03T17:15:43Z", | ||
| 79 | + "position":0, | ||
| 80 | + "branch_name":null, | ||
| 81 | + "description":"Create new API for manipulations with repository", | ||
| 82 | + "milestone_id":null, | ||
| 83 | + "state":"opened", | ||
| 84 | + "iid":23 | ||
| 85 | + } | ||
| 86 | + } | ||
| 87 | + %h4.prepend-top-20 Merge request events | ||
| 88 | + %p.light | ||
| 89 | + Triggered when new merge request created or existing merge request was merged/closed. | ||
| 90 | + %br | ||
| 91 | + Request body: | ||
| 92 | + = highlight_js do | ||
| 93 | + :erb | ||
| 94 | + { | ||
| 95 | + "object_kind":"merge_request", | ||
| 96 | + "object_attributes":{ | ||
| 97 | + "id":99, | ||
| 98 | + "target_branch":"master", | ||
| 99 | + "source_branch":"ms-viewport", | ||
| 100 | + "source_project_id":14, | ||
| 101 | + "author_id":51, | ||
| 102 | + "assignee_id":6, | ||
| 103 | + "title":"MS-Viewport", | ||
| 104 | + "created_at":"2013-12-03T17:23:34Z", | ||
| 105 | + "updated_at":"2013-12-03T17:23:34Z", | ||
| 106 | + "st_commits":null, | ||
| 107 | + "st_diffs":null, | ||
| 108 | + "milestone_id":null, | ||
| 109 | + "state":"opened", | ||
| 110 | + "merge_status":"unchecked", | ||
| 111 | + "target_project_id":14, | ||
| 112 | + "iid":1, | ||
| 113 | + "description":"" | ||
| 114 | + } | ||
| 115 | + } |
app/views/layouts/application.html.haml
| @@ -2,6 +2,7 @@ | @@ -2,6 +2,7 @@ | ||
| 2 | %html{ lang: "en"} | 2 | %html{ lang: "en"} |
| 3 | = render "layouts/head", title: "Dashboard" | 3 | = render "layouts/head", title: "Dashboard" |
| 4 | %body{class: "#{app_theme} application", :'data-page' => body_data_page } | 4 | %body{class: "#{app_theme} application", :'data-page' => body_data_page } |
| 5 | + = render "layouts/broadcast" | ||
| 5 | = render "layouts/head_panel", title: "Dashboard" | 6 | = render "layouts/head_panel", title: "Dashboard" |
| 6 | = render "layouts/flash" | 7 | = render "layouts/flash" |
| 7 | %nav.main-nav | 8 | %nav.main-nav |
app/views/layouts/group.html.haml
| 1 | !!! 5 | 1 | !!! 5 |
| 2 | %html{ lang: "en"} | 2 | %html{ lang: "en"} |
| 3 | - = render "layouts/head", title: "#{@group.name}" | 3 | + = render "layouts/head", title: group_head_title |
| 4 | %body{class: "#{app_theme} application", :'data-page' => body_data_page} | 4 | %body{class: "#{app_theme} application", :'data-page' => body_data_page} |
| 5 | + = render "layouts/broadcast" | ||
| 5 | = render "layouts/head_panel", title: "group: #{@group.name}" | 6 | = render "layouts/head_panel", title: "group: #{@group.name}" |
| 6 | = render "layouts/flash" | 7 | = render "layouts/flash" |
| 7 | %nav.main-nav | 8 | %nav.main-nav |
app/views/layouts/nav/_admin.html.haml
| @@ -10,6 +10,8 @@ | @@ -10,6 +10,8 @@ | ||
| 10 | = link_to "Users", admin_users_path | 10 | = link_to "Users", admin_users_path |
| 11 | = nav_link(controller: :logs) do | 11 | = nav_link(controller: :logs) do |
| 12 | = link_to "Logs", admin_logs_path | 12 | = link_to "Logs", admin_logs_path |
| 13 | + = nav_link(controller: :broadcast_messages) do | ||
| 14 | + = link_to "Messages", admin_broadcast_messages_path | ||
| 13 | = nav_link(controller: :hooks) do | 15 | = nav_link(controller: :hooks) do |
| 14 | = link_to "Hooks", admin_hooks_path | 16 | = link_to "Hooks", admin_hooks_path |
| 15 | = nav_link(controller: :background_jobs) do | 17 | = nav_link(controller: :background_jobs) do |
app/views/layouts/profile.html.haml
| @@ -2,6 +2,7 @@ | @@ -2,6 +2,7 @@ | ||
| 2 | %html{ lang: "en"} | 2 | %html{ lang: "en"} |
| 3 | = render "layouts/head", title: "Profile" | 3 | = render "layouts/head", title: "Profile" |
| 4 | %body{class: "#{app_theme} profile", :'data-page' => body_data_page} | 4 | %body{class: "#{app_theme} profile", :'data-page' => body_data_page} |
| 5 | + = render "layouts/broadcast" | ||
| 5 | = render "layouts/head_panel", title: "Profile" | 6 | = render "layouts/head_panel", title: "Profile" |
| 6 | = render "layouts/flash" | 7 | = render "layouts/flash" |
| 7 | %nav.main-nav | 8 | %nav.main-nav |
app/views/layouts/project_settings.html.haml
| @@ -2,6 +2,7 @@ | @@ -2,6 +2,7 @@ | ||
| 2 | %html{ lang: "en"} | 2 | %html{ lang: "en"} |
| 3 | = render "layouts/head", title: @project.name_with_namespace | 3 | = render "layouts/head", title: @project.name_with_namespace |
| 4 | %body{class: "#{app_theme} project", :'data-page' => body_data_page, :'data-project-id' => @project.id } | 4 | %body{class: "#{app_theme} project", :'data-page' => body_data_page, :'data-project-id' => @project.id } |
| 5 | + = render "layouts/broadcast" | ||
| 5 | = render "layouts/head_panel", title: project_title(@project) | 6 | = render "layouts/head_panel", title: project_title(@project) |
| 6 | = render "layouts/init_auto_complete" | 7 | = render "layouts/init_auto_complete" |
| 7 | = render "layouts/flash" | 8 | = render "layouts/flash" |
app/views/layouts/projects.html.haml
| 1 | !!! 5 | 1 | !!! 5 |
| 2 | %html{ lang: "en"} | 2 | %html{ lang: "en"} |
| 3 | - = render "layouts/head", title: @project.name_with_namespace | 3 | + = render "layouts/head", title: project_head_title |
| 4 | %body{class: "#{app_theme} project", :'data-page' => body_data_page, :'data-project-id' => @project.id } | 4 | %body{class: "#{app_theme} project", :'data-page' => body_data_page, :'data-project-id' => @project.id } |
| 5 | + = render "layouts/broadcast" | ||
| 5 | = render "layouts/head_panel", title: project_title(@project) | 6 | = render "layouts/head_panel", title: project_title(@project) |
| 6 | = render "layouts/init_auto_complete" | 7 | = render "layouts/init_auto_complete" |
| 7 | = render "layouts/flash" | 8 | = render "layouts/flash" |
app/views/layouts/public.html.haml
| 1 | !!! 5 | 1 | !!! 5 |
| 2 | %html{ lang: "en"} | 2 | %html{ lang: "en"} |
| 3 | = render "layouts/head", title: "Public Projects" | 3 | = render "layouts/head", title: "Public Projects" |
| 4 | - %body{class: "ui_mars application", :'data-page' => body_data_page} | 4 | + %body{class: "#{app_theme} application", :'data-page' => body_data_page} |
| 5 | - if current_user | 5 | - if current_user |
| 6 | = render "layouts/head_panel", title: "Public Projects" | 6 | = render "layouts/head_panel", title: "Public Projects" |
| 7 | - else | 7 | - else |
app/views/layouts/public_projects.html.haml
| 1 | !!! 5 | 1 | !!! 5 |
| 2 | %html{ lang: "en"} | 2 | %html{ lang: "en"} |
| 3 | = render "layouts/head", title: @project.name_with_namespace | 3 | = render "layouts/head", title: @project.name_with_namespace |
| 4 | - %body{class: "ui_mars application", :'data-page' => body_data_page} | 4 | + %body{class: "#{app_theme} application", :'data-page' => body_data_page} |
| 5 | = render "layouts/public_head_panel" | 5 | = render "layouts/public_head_panel" |
| 6 | %nav.main-nav | 6 | %nav.main-nav |
| 7 | .container= render 'layouts/nav/project' | 7 | .container= render 'layouts/nav/project' |
app/views/notify/_note_message.html.haml
app/views/notify/new_merge_request_email.html.haml
| 1 | %p | 1 | %p |
| 2 | - = "New Merge Request !#{@merge_request.iid}" | 2 | + = "New Merge Request ##{@merge_request.iid}" |
| 3 | %p | 3 | %p |
| 4 | = link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.target_project, @merge_request) | 4 | = link_to_gfm truncate(@merge_request.title, length: 40), project_merge_request_url(@merge_request.target_project, @merge_request) |
| 5 | %p | 5 | %p |
app/views/notify/new_merge_request_email.text.erb
app/views/notify/reassigned_merge_request_email.html.haml
| 1 | %p | 1 | %p |
| 2 | - = "Reassigned Merge Request !#{@merge_request.iid}" | 2 | + = "Reassigned Merge Request ##{@merge_request.iid}" |
| 3 | = link_to_gfm truncate(@merge_request.title, length: 30), project_merge_request_url(@merge_request.target_project, @merge_request) | 3 | = link_to_gfm truncate(@merge_request.title, length: 30), project_merge_request_url(@merge_request.target_project, @merge_request) |
| 4 | %p | 4 | %p |
| 5 | Assignee changed | 5 | Assignee changed |
app/views/notify/reassigned_merge_request_email.text.erb
app/views/profiles/show.html.haml
| @@ -21,16 +21,22 @@ | @@ -21,16 +21,22 @@ | ||
| 21 | .controls | 21 | .controls |
| 22 | = f.text_field :name, class: "input-xlarge", required: true | 22 | = f.text_field :name, class: "input-xlarge", required: true |
| 23 | %span.help-block Enter your name, so people you know can recognize you. | 23 | %span.help-block Enter your name, so people you know can recognize you. |
| 24 | + | ||
| 24 | .control-group | 25 | .control-group |
| 25 | = f.label :email, class: "control-label" | 26 | = f.label :email, class: "control-label" |
| 26 | .controls | 27 | .controls |
| 27 | - = f.text_field :email, class: "input-xlarge", required: true | ||
| 28 | - - if @user.unconfirmed_email.present? | ||
| 29 | - %span.help-block | ||
| 30 | - We sent confirmation email to | ||
| 31 | - %strong #{@user.unconfirmed_email} | 28 | + - if @user.ldap_user? |
| 29 | + = f.text_field :email, class: "input-xlarge", required: true, readonly: true | ||
| 30 | + %span.help-block.light | ||
| 31 | + Email is read-only for LDAP user | ||
| 32 | - else | 32 | - else |
| 33 | - %span.help-block We also use email for avatar detection if no avatar is uploaded. | 33 | + = f.text_field :email, class: "input-xlarge", required: true |
| 34 | + - if @user.unconfirmed_email.present? | ||
| 35 | + %span.help-block | ||
| 36 | + We sent confirmation email to | ||
| 37 | + %strong #{@user.unconfirmed_email} | ||
| 38 | + - else | ||
| 39 | + %span.help-block We also use email for avatar detection if no avatar is uploaded. | ||
| 34 | .control-group | 40 | .control-group |
| 35 | = f.label :skype, class: "control-label" | 41 | = f.label :skype, class: "control-label" |
| 36 | .controls= f.text_field :skype, class: "input-xlarge" | 42 | .controls= f.text_field :skype, class: "input-xlarge" |
| @@ -53,9 +59,14 @@ | @@ -53,9 +59,14 @@ | ||
| 53 | .clearfix | 59 | .clearfix |
| 54 | .profile-avatar-form-option | 60 | .profile-avatar-form-option |
| 55 | %p.light | 61 | %p.light |
| 56 | - You can upload an avatar here | ||
| 57 | - %br | ||
| 58 | - or change it at #{link_to "gravatar.com", "http://gravatar.com"} | 62 | + - if @user.avatar? |
| 63 | + You can change your avatar here | ||
| 64 | + %br | ||
| 65 | + or remove the current avatar to revert to #{link_to "gravatar.com", "http://gravatar.com"} | ||
| 66 | + - else | ||
| 67 | + You can upload an avatar here | ||
| 68 | + %br | ||
| 69 | + or change it at #{link_to "gravatar.com", "http://gravatar.com"} | ||
| 59 | %hr | 70 | %hr |
| 60 | %a.choose-btn.btn.btn-small.js-choose-user-avatar-button | 71 | %a.choose-btn.btn.btn-small.js-choose-user-avatar-button |
| 61 | %i.icon-paper-clip | 72 | %i.icon-paper-clip |
| @@ -63,7 +74,10 @@ | @@ -63,7 +74,10 @@ | ||
| 63 | | 74 | |
| 64 | %span.file_name.js-avatar-filename File name... | 75 | %span.file_name.js-avatar-filename File name... |
| 65 | = f.file_field :avatar, class: "js-user-avatar-input hide" | 76 | = f.file_field :avatar, class: "js-user-avatar-input hide" |
| 66 | - %span.help-block The maximum file size allowed is 100KB. | 77 | + .light The maximum file size allowed is 100KB. |
| 78 | + - if @user.avatar? | ||
| 79 | + %hr | ||
| 80 | + = link_to 'Remove avatar', profile_avatar_path, confirm: "Avatar will be removed. Are you sure?", method: :delete, class: "btn btn-remove btn-small remove-avatar" | ||
| 67 | 81 | ||
| 68 | .form-actions | 82 | .form-actions |
| 69 | = f.submit 'Save changes', class: "btn btn-save" | 83 | = f.submit 'Save changes', class: "btn btn-save" |
app/views/projects/_dropdown.html.haml
| @@ -6,15 +6,19 @@ | @@ -6,15 +6,19 @@ | ||
| 6 | - if @project.issues_enabled && can?(current_user, :write_issue, @project) | 6 | - if @project.issues_enabled && can?(current_user, :write_issue, @project) |
| 7 | %li | 7 | %li |
| 8 | = link_to url_for_new_issue, title: "New Issue" do | 8 | = link_to url_for_new_issue, title: "New Issue" do |
| 9 | - Issue | 9 | + New issue |
| 10 | - if @project.merge_requests_enabled && can?(current_user, :write_merge_request, @project) | 10 | - if @project.merge_requests_enabled && can?(current_user, :write_merge_request, @project) |
| 11 | %li | 11 | %li |
| 12 | = link_to new_project_merge_request_path(@project), title: "New Merge Request" do | 12 | = link_to new_project_merge_request_path(@project), title: "New Merge Request" do |
| 13 | - Merge Request | 13 | + New merge request |
| 14 | - if @project.snippets_enabled && can?(current_user, :write_snippet, @project) | 14 | - if @project.snippets_enabled && can?(current_user, :write_snippet, @project) |
| 15 | %li | 15 | %li |
| 16 | = link_to new_project_snippet_path(@project), title: "New Snippet" do | 16 | = link_to new_project_snippet_path(@project), title: "New Snippet" do |
| 17 | - Snippet | 17 | + New snippet |
| 18 | + - if can?(current_user, :admin_team_member, @project) | ||
| 19 | + %li | ||
| 20 | + = link_to new_project_team_member_path(@project), title: "New project member" do | ||
| 21 | + New project member | ||
| 18 | - if can? current_user, :push_code, @project | 22 | - if can? current_user, :push_code, @project |
| 19 | %li.divider | 23 | %li.divider |
| 20 | %li | 24 | %li |
| @@ -26,9 +30,4 @@ | @@ -26,9 +30,4 @@ | ||
| 26 | %i.icon-tag | 30 | %i.icon-tag |
| 27 | Git tag | 31 | Git tag |
| 28 | 32 | ||
| 29 | - - if can?(current_user, :admin_team_member, @project) | ||
| 30 | - %li.divider | ||
| 31 | - %li | ||
| 32 | - = link_to new_project_team_member_path(@project), title: "New project member" do | ||
| 33 | - Project member | ||
| 34 | 33 |
| @@ -0,0 +1,31 @@ | @@ -0,0 +1,31 @@ | ||
| 1 | +- empty_repo = @project.empty_repo? | ||
| 2 | +.project-home-panel{:class => ("empty-project" if empty_repo)} | ||
| 3 | + .row | ||
| 4 | + .span5 | ||
| 5 | + %h4.project-home-title | ||
| 6 | + = @project.name_with_namespace | ||
| 7 | + %span.visibility-level-label | ||
| 8 | + = visibility_level_icon(@project.visibility_level) | ||
| 9 | + = visibility_level_label(@project.visibility_level) | ||
| 10 | + | ||
| 11 | + .span7 | ||
| 12 | + - unless empty_repo | ||
| 13 | + .project-home-dropdown | ||
| 14 | + = render "dropdown" | ||
| 15 | + .form-horizontal | ||
| 16 | + = render "shared/clone_panel" | ||
| 17 | + | ||
| 18 | + .project-home-extra.clearfix | ||
| 19 | + .project-home-desc | ||
| 20 | + - if @project.description.present? | ||
| 21 | + = @project.description | ||
| 22 | + - if can?(current_user, :admin_project, @project) | ||
| 23 | + – | ||
| 24 | + %strong= link_to 'Edit', edit_project_path | ||
| 25 | + | ||
| 26 | + - unless empty_repo | ||
| 27 | + .project-home-links | ||
| 28 | + = link_to pluralize(@repository.round_commit_count, 'commit'), project_commits_path(@project, @ref || @repository.root_ref) | ||
| 29 | + = link_to pluralize(@repository.branch_names.count, 'branch'), project_branches_path(@project) | ||
| 30 | + = link_to pluralize(@repository.tag_names.count, 'tag'), project_tags_path(@project) | ||
| 31 | + %span.light.prepend-left-20= repository_size | ||
| 0 | \ No newline at end of file | 32 | \ No newline at end of file |
| @@ -0,0 +1,26 @@ | @@ -0,0 +1,26 @@ | ||
| 1 | +.control-group.project-visibility-level-holder | ||
| 2 | + = f.label :visibility_level do | ||
| 3 | + Visibility Level | ||
| 4 | + = link_to "(?)", help_public_access_path | ||
| 5 | + - if can_change_visibility_level | ||
| 6 | + - Gitlab::VisibilityLevel.values.each do |level| | ||
| 7 | + - restricted = restricted_visibility_levels.include?(level) | ||
| 8 | + .controls | ||
| 9 | + = f.radio_button :visibility_level, level, checked: (visibility_level == level), disabled: restricted | ||
| 10 | + %span.descr{:class => ("restricted" if restricted)} | ||
| 11 | + = label :project_visibility_level, level do | ||
| 12 | + = visibility_level_icon(level) | ||
| 13 | + %strong | ||
| 14 | + = visibility_level_label(level) | ||
| 15 | + .light= visibility_level_description(level) | ||
| 16 | + - unless restricted_visibility_levels.empty? | ||
| 17 | + .controls | ||
| 18 | + %span.info | ||
| 19 | + Some visibility level settings have been restricted by the administrator. | ||
| 20 | + - else | ||
| 21 | + .controls | ||
| 22 | + %span.info | ||
| 23 | + = visibility_level_icon(visibility_level) | ||
| 24 | + %strong | ||
| 25 | + = visibility_level_label(visibility_level) | ||
| 26 | + .light= visibility_level_description(visibility_level) |
app/views/projects/blob/_actions.html.haml
| @@ -4,7 +4,7 @@ | @@ -4,7 +4,7 @@ | ||
| 4 | - if allowed_tree_edit? | 4 | - if allowed_tree_edit? |
| 5 | = link_to "edit", project_edit_tree_path(@project, @id), class: "btn btn-small" | 5 | = link_to "edit", project_edit_tree_path(@project, @id), class: "btn btn-small" |
| 6 | - else | 6 | - else |
| 7 | - %span.btn.btn-small.disabled Edit | 7 | + %span.btn.btn-small.disabled edit |
| 8 | = link_to "raw", project_raw_path(@project, @id), class: "btn btn-small", target: "_blank" | 8 | = link_to "raw", project_raw_path(@project, @id), class: "btn btn-small", target: "_blank" |
| 9 | -# only show normal/blame view links for text files | 9 | -# only show normal/blame view links for text files |
| 10 | - if @blob.text? | 10 | - if @blob.text? |
| @@ -13,3 +13,7 @@ | @@ -13,3 +13,7 @@ | ||
| 13 | - else | 13 | - else |
| 14 | = link_to "blame", project_blame_path(@project, @id), class: "btn btn-small" unless @blob.empty? | 14 | = link_to "blame", project_blame_path(@project, @id), class: "btn btn-small" unless @blob.empty? |
| 15 | = link_to "history", project_commits_path(@project, @id), class: "btn btn-small" | 15 | = link_to "history", project_commits_path(@project, @id), class: "btn btn-small" |
| 16 | + | ||
| 17 | + - if allowed_tree_edit? | ||
| 18 | + = link_to '#modal-remove-blob', class: "remove-blob btn btn-small btn-remove", "data-toggle" => "modal" do | ||
| 19 | + remove |
| @@ -0,0 +1,19 @@ | @@ -0,0 +1,19 @@ | ||
| 1 | +%div#modal-remove-blob.modal.hide | ||
| 2 | + .modal-header | ||
| 3 | + %a.close{href: "#", "data-dismiss" => "modal"} × | ||
| 4 | + %h3.page-title Remove #{@blob.name} | ||
| 5 | + %p.light | ||
| 6 | + From branch | ||
| 7 | + %strong= @ref | ||
| 8 | + | ||
| 9 | + .modal-body | ||
| 10 | + = form_tag project_blob_path(@project, @id), method: :delete do | ||
| 11 | + .control-group.commit_message-group | ||
| 12 | + = label_tag 'commit_message', class: "control-label" do | ||
| 13 | + Commit message | ||
| 14 | + .controls | ||
| 15 | + = text_area_tag 'commit_message', params[:commit_message], placeholder: "Removed this file because...", required: true, rows: 3 | ||
| 16 | + .control-group | ||
| 17 | + .controls | ||
| 18 | + = submit_tag 'Remove file', class: 'btn btn-remove' | ||
| 19 | + = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal" |
app/views/projects/blob/show.html.haml
| @@ -2,3 +2,6 @@ | @@ -2,3 +2,6 @@ | ||
| 2 | = render 'shared/ref_switcher', destination: 'blob', path: @path | 2 | = render 'shared/ref_switcher', destination: 'blob', path: @path |
| 3 | %div#tree-holder.tree-holder | 3 | %div#tree-holder.tree-holder |
| 4 | = render 'blob', blob: @blob | 4 | = render 'blob', blob: @blob |
| 5 | + | ||
| 6 | +- if allowed_tree_edit? | ||
| 7 | + = render 'projects/blob/remove' |
app/views/projects/branches/_filter.html.haml
| 1 | %ul.nav.nav-pills.nav-stacked | 1 | %ul.nav.nav-pills.nav-stacked |
| 2 | = nav_link(path: 'branches#recent') do | 2 | = nav_link(path: 'branches#recent') do |
| 3 | - = link_to 'Recent', recent_project_branches_path(@project) | 3 | + = link_to recent_project_branches_path(@project) do |
| 4 | + Recent | ||
| 5 | + .pull-right | ||
| 6 | + = @repository.recent_branches.count | ||
| 7 | + | ||
| 4 | = nav_link(path: 'protected_branches#index') do | 8 | = nav_link(path: 'protected_branches#index') do |
| 5 | = link_to project_protected_branches_path(@project) do | 9 | = link_to project_protected_branches_path(@project) do |
| 6 | Protected | 10 | Protected |
| 7 | %i.icon-lock | 11 | %i.icon-lock |
| 12 | + .pull-right | ||
| 13 | + = @project.protected_branches.count | ||
| 14 | + | ||
| 8 | = nav_link(path: 'branches#index') do | 15 | = nav_link(path: 'branches#index') do |
| 9 | - = link_to 'All branches', project_branches_path(@project) | 16 | + = link_to project_branches_path(@project) do |
| 17 | + All branches | ||
| 18 | + .pull-right | ||
| 19 | + = @repository.branch_names.count | ||
| 10 | 20 | ||
| 11 | 21 | ||
| 12 | %hr | 22 | %hr |
app/views/projects/commits/_commit.html.haml
| @@ -7,7 +7,7 @@ | @@ -7,7 +7,7 @@ | ||
| 7 | .notes_count | 7 | .notes_count |
| 8 | - notes = project.notes.for_commit_id(commit.id) | 8 | - notes = project.notes.for_commit_id(commit.id) |
| 9 | - if notes.any? | 9 | - if notes.any? |
| 10 | - %span.badge.badge-info | 10 | + %span.label.label-gray |
| 11 | %i.icon-comment | 11 | %i.icon-comment |
| 12 | = notes.count | 12 | = notes.count |
| 13 | 13 |
app/views/projects/commits/_diffs.html.haml
| @@ -30,6 +30,10 @@ | @@ -30,6 +30,10 @@ | ||
| 30 | %strong.cgreen #{@commit.stats.additions} additions | 30 | %strong.cgreen #{@commit.stats.additions} additions |
| 31 | and | 31 | and |
| 32 | %strong.cred #{@commit.stats.deletions} deletions | 32 | %strong.cred #{@commit.stats.deletions} deletions |
| 33 | + - if params[:view] == 'parallel' | ||
| 34 | + = link_to "Inline Diff", url_for(view: 'inline'), {id: "commit-diff-viewtype", class: 'btn btn-tiny pull-right'} | ||
| 35 | + - else | ||
| 36 | + = link_to "Side-by-side Diff", url_for(view: 'parallel'), {id: "commit-diff-viewtype", class: 'btn btn-tiny pull-right'} | ||
| 33 | .file-stats | 37 | .file-stats |
| 34 | = render "projects/commits/diff_head", diffs: diffs | 38 | = render "projects/commits/diff_head", diffs: diffs |
| 35 | 39 | ||
| @@ -46,7 +50,7 @@ | @@ -46,7 +50,7 @@ | ||
| 46 | %span= diff.old_path | 50 | %span= diff.old_path |
| 47 | 51 | ||
| 48 | - if @commit.parent_ids.present? | 52 | - if @commit.parent_ids.present? |
| 49 | - = link_to project_blob_path(project, tree_join(@commit.parent_id, diff.new_path)), {:class => 'btn btn-tiny pull-right view-file'} do | 53 | + = link_to project_blob_path(project, tree_join(@commit.parent_id, diff.new_path)), { class: 'btn btn-small view-file' } do |
| 50 | View file @ | 54 | View file @ |
| 51 | %span.commit-short-id= @commit.short_id(6) | 55 | %span.commit-short-id= @commit.short_id(6) |
| 52 | - else | 56 | - else |
| @@ -54,7 +58,7 @@ | @@ -54,7 +58,7 @@ | ||
| 54 | - if diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode | 58 | - if diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode |
| 55 | %span.file-mode= "#{diff.a_mode} → #{diff.b_mode}" | 59 | %span.file-mode= "#{diff.a_mode} → #{diff.b_mode}" |
| 56 | 60 | ||
| 57 | - = link_to project_blob_path(project, tree_join(@commit.id, diff.new_path)), {:class => 'btn btn-tiny pull-right view-file'} do | 61 | + = link_to project_blob_path(project, tree_join(@commit.id, diff.new_path)), { class: 'btn btn-small view-file' } do |
| 58 | View file @ | 62 | View file @ |
| 59 | %span.commit-short-id= @commit.short_id(6) | 63 | %span.commit-short-id= @commit.short_id(6) |
| 60 | 64 | ||
| @@ -62,7 +66,10 @@ | @@ -62,7 +66,10 @@ | ||
| 62 | -# Skipp all non non-supported blobs | 66 | -# Skipp all non non-supported blobs |
| 63 | - next unless file.respond_to?('text?') | 67 | - next unless file.respond_to?('text?') |
| 64 | - if file.text? | 68 | - if file.text? |
| 65 | - = render "projects/commits/text_file", diff: diff, index: i | 69 | + - if params[:view] == 'parallel' |
| 70 | + = render "projects/commits/parallel_view", diff: diff, project: project, file: file, index: i | ||
| 71 | + - else | ||
| 72 | + = render "projects/commits/text_file", diff: diff, index: i | ||
| 66 | - elsif file.image? | 73 | - elsif file.image? |
| 67 | - old_file = project.repository.blob_at(@commit.parent_id, diff.old_path) if @commit.parent_id | 74 | - old_file = project.repository.blob_at(@commit.parent_id, diff.old_path) if @commit.parent_id |
| 68 | = render "projects/commits/image", diff: diff, old_file: old_file, file: file, index: i | 75 | = render "projects/commits/image", diff: diff, old_file: old_file, file: file, index: i |
| @@ -0,0 +1,75 @@ | @@ -0,0 +1,75 @@ | ||
| 1 | +/ Side-by-side diff view | ||
| 2 | +- old_file = get_old_file(project, @commit, diff) | ||
| 3 | +- deleted_lines = {} | ||
| 4 | +- added_lines = {} | ||
| 5 | +- each_diff_line(diff, index) do |line, type, line_code, line_new, line_old, raw_line| | ||
| 6 | + - if type == "old" | ||
| 7 | + - deleted_lines[line_old] = { line_code: line_code, type: type, line: line } | ||
| 8 | + - elsif type == "new" | ||
| 9 | + - added_lines[line_new] = { line_code: line_code, type: type, line: line } | ||
| 10 | + | ||
| 11 | +- max_length = old_file.sloc + added_lines.length if old_file | ||
| 12 | +- max_length ||= file.sloc | ||
| 13 | +- offset1 = 0 | ||
| 14 | +- offset2 = 0 | ||
| 15 | + | ||
| 16 | +%div.text-file-parallel | ||
| 17 | + %table{ style: "table-layout: fixed;" } | ||
| 18 | + - max_length.times do |line_index| | ||
| 19 | + - line_index1 = line_index - offset1 | ||
| 20 | + - line_index2 = line_index - offset2 | ||
| 21 | + - deleted_line = deleted_lines[line_index1 + 1] | ||
| 22 | + - added_line = added_lines[line_index2 + 1] | ||
| 23 | + - old_line = old_file.lines[line_index1] if old_file | ||
| 24 | + - new_line = file.lines[line_index2] | ||
| 25 | + | ||
| 26 | + - if deleted_line && added_line | ||
| 27 | + - elsif deleted_line | ||
| 28 | + - new_line = nil | ||
| 29 | + - offset2 += 1 | ||
| 30 | + - elsif added_line | ||
| 31 | + - old_line = nil | ||
| 32 | + - offset1 += 1 | ||
| 33 | + | ||
| 34 | + %tr.line_holder.parallel | ||
| 35 | + - if line_index == 0 && diff.new_file | ||
| 36 | + %td.line_content.parallel= "File was created" | ||
| 37 | + %td.old_line= "" | ||
| 38 | + - elsif deleted_line | ||
| 39 | + %td.line_content{class: "parallel noteable_line old #{deleted_line[:line_code]}", "line_code" => deleted_line[:line_code] }= old_line | ||
| 40 | + %td.old_line.old | ||
| 41 | + = line_index1 + 1 | ||
| 42 | + - if @comments_allowed | ||
| 43 | + =# render "projects/notes/diff_note_link", line_code: deleted_line[:line_code] | ||
| 44 | + - elsif old_line | ||
| 45 | + %td.line_content.parallel= old_line | ||
| 46 | + %td.old_line= line_index1 + 1 | ||
| 47 | + - else | ||
| 48 | + %td.line_content.parallel= "" | ||
| 49 | + %td.old_line= "" | ||
| 50 | + | ||
| 51 | + %td.diff_line= "" | ||
| 52 | + | ||
| 53 | + - if diff.deleted_file && line_index == 0 | ||
| 54 | + %td.new_line= "" | ||
| 55 | + %td.line_content.parallel= "File was deleted" | ||
| 56 | + - elsif added_line | ||
| 57 | + %td.new_line.new | ||
| 58 | + = line_index2 + 1 | ||
| 59 | + - if @comments_allowed | ||
| 60 | + =# render "projects/notes/diff_note_link", line_code: added_line[:line_code] | ||
| 61 | + %td.line_content{class: "parallel noteable_line new #{added_line[:line_code]}", "line_code" => added_line[:line_code] }= new_line | ||
| 62 | + - elsif new_line | ||
| 63 | + %td.new_line= line_index2 + 1 | ||
| 64 | + %td.line_content.parallel= new_line | ||
| 65 | + - else | ||
| 66 | + %td.new_line= "" | ||
| 67 | + %td.line_content.parallel= "" | ||
| 68 | + | ||
| 69 | + - if @reply_allowed | ||
| 70 | + - comments1 = [] | ||
| 71 | + - comments2 = [] | ||
| 72 | + - comments1 = @line_notes.select { |n| n.line_code == deleted_line[:line_code] }.sort_by(&:created_at) if deleted_line | ||
| 73 | + - comments2 = @line_notes.select { |n| n.line_code == added_line[:line_code] }.sort_by(&:created_at) if added_line | ||
| 74 | + - unless comments1.empty? && comments2.empty? | ||
| 75 | + = render "projects/notes/diff_notes_with_reply_parallel", notes1: comments1, notes2: comments2, line1: deleted_line, line2: added_line | ||
| 0 | \ No newline at end of file | 76 | \ No newline at end of file |
app/views/projects/commits/_text_file.html.haml
| @@ -21,3 +21,4 @@ | @@ -21,3 +21,4 @@ | ||
| 21 | - comments = @line_notes.select { |n| n.line_code == line_code }.sort_by(&:created_at) | 21 | - comments = @line_notes.select { |n| n.line_code == line_code }.sort_by(&:created_at) |
| 22 | - unless comments.empty? | 22 | - unless comments.empty? |
| 23 | = render "projects/notes/diff_notes_with_reply", notes: comments, line: line | 23 | = render "projects/notes/diff_notes_with_reply", notes: comments, line: line |
| 24 | + |
app/views/projects/commits/show.js.haml
app/views/projects/edit.html.haml
| @@ -29,22 +29,7 @@ | @@ -29,22 +29,7 @@ | ||
| 29 | .controls= f.select(:default_branch, @repository.branch_names, {}, {class: 'chosen'}) | 29 | .controls= f.select(:default_branch, @repository.branch_names, {}, {class: 'chosen'}) |
| 30 | 30 | ||
| 31 | 31 | ||
| 32 | - - if can?(current_user, :change_public_mode, @project) | ||
| 33 | - %fieldset.public-mode | ||
| 34 | - %legend | ||
| 35 | - Public mode: | ||
| 36 | - .control-group | ||
| 37 | - = f.label :public, class: 'control-label' do | ||
| 38 | - %span Public access | ||
| 39 | - .controls | ||
| 40 | - = f.check_box :public | ||
| 41 | - %span.descr | ||
| 42 | - If checked, this project can be cloned | ||
| 43 | - %em without any | ||
| 44 | - authentication. | ||
| 45 | - It will also be listed on the #{link_to "public access directory", public_root_path}. | ||
| 46 | - %em Any | ||
| 47 | - user will have #{link_to "Guest", help_permissions_path} permissions on the repository. | 32 | + = render "visibility_level", f: f, visibility_level: @project.visibility_level, can_change_visibility_level: can?(current_user, :change_visibility_level, @project) |
| 48 | 33 | ||
| 49 | %fieldset.features | 34 | %fieldset.features |
| 50 | %legend | 35 | %legend |
| @@ -124,7 +109,7 @@ | @@ -124,7 +109,7 @@ | ||
| 124 | %span Namespace | 109 | %span Namespace |
| 125 | .controls | 110 | .controls |
| 126 | .control-group | 111 | .control-group |
| 127 | - = f.select :namespace_id, namespaces_options(@project.namespace_id), {prompt: 'Choose a project namespace'}, {class: 'chosen'} | 112 | + = f.select :namespace_id, namespaces_options(@project.namespace_id), { prompt: 'Choose a project namespace' }, { class: 'chosen' } |
| 128 | %ul | 113 | %ul |
| 129 | %li Be careful. Changing the project's namespace can have unintended side effects. | 114 | %li Be careful. Changing the project's namespace can have unintended side effects. |
| 130 | %li You can only transfer the project to namespaces you manage. | 115 | %li You can only transfer the project to namespaces you manage. |
| @@ -144,7 +129,9 @@ | @@ -144,7 +129,9 @@ | ||
| 144 | %span Path | 129 | %span Path |
| 145 | .controls | 130 | .controls |
| 146 | .control-group | 131 | .control-group |
| 147 | - = f.text_field :path | 132 | + .input-append |
| 133 | + = f.text_field :path | ||
| 134 | + %span.add-on .git | ||
| 148 | %ul | 135 | %ul |
| 149 | %li Be careful. Renaming a project's repository can have unintended side effects. | 136 | %li Be careful. Renaming a project's repository can have unintended side effects. |
| 150 | %li You will need to update your local repositories to point to the new location. | 137 | %li You will need to update your local repositories to point to the new location. |
app/views/projects/empty.html.haml
app/views/projects/fork.html.haml
| @@ -3,9 +3,9 @@ | @@ -3,9 +3,9 @@ | ||
| 3 | %i.icon-code-fork | 3 | %i.icon-code-fork |
| 4 | Fork Error! | 4 | Fork Error! |
| 5 | %p | 5 | %p |
| 6 | - You are trying to fork | 6 | + You tried to fork |
| 7 | = link_to_project @project | 7 | = link_to_project @project |
| 8 | - but it fails due to next reason: | 8 | + but it failed for the following reason: |
| 9 | 9 | ||
| 10 | 10 | ||
| 11 | - if @forked_project && @forked_project.errors.any? | 11 | - if @forked_project && @forked_project.errors.any? |
app/views/projects/hooks/_data_ex.html.erb
| @@ -1,44 +0,0 @@ | @@ -1,44 +0,0 @@ | ||
| 1 | -<% data_ex_str = <<eos | ||
| 2 | -{ | ||
| 3 | - "before": "95790bf891e76fee5e1747ab589903a6a1f80f22", | ||
| 4 | - "after": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", | ||
| 5 | - "ref": "refs/heads/master", | ||
| 6 | - "user_id": 4, | ||
| 7 | - "user_name": "John Smith", | ||
| 8 | - "project_id": 15, | ||
| 9 | - "repository": { | ||
| 10 | - "name": "Diaspora", | ||
| 11 | - "url": "git@localhost:diaspora.git", | ||
| 12 | - "description": "", | ||
| 13 | - "homepage": "http://localhost/diaspora", | ||
| 14 | - }, | ||
| 15 | - "commits": [ | ||
| 16 | - { | ||
| 17 | - "id": "b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", | ||
| 18 | - "message": "Update Catalan translation to e38cb41.", | ||
| 19 | - "timestamp": "2011-12-12T14:27:31+02:00", | ||
| 20 | - "url": "http://localhost/diaspora/commits/b6568db1bc1dcd7f8b4d5a946b0b91f9dacd7327", | ||
| 21 | - "author": { | ||
| 22 | - "name": "Jordi Mallach", | ||
| 23 | - "email": "jordi@softcatala.org", | ||
| 24 | - } | ||
| 25 | - }, | ||
| 26 | - // ... | ||
| 27 | - { | ||
| 28 | - "id": "da1560886d4f094c3e6c9ef40349f7d38b5d27d7", | ||
| 29 | - "message": "fixed readme", | ||
| 30 | - "timestamp": "2012-01-03T23:36:29+02:00", | ||
| 31 | - "url": "http://localhost/diaspora/commits/da1560886d4f094c3e6c9ef40349f7d38b5d27d7", | ||
| 32 | - "author": { | ||
| 33 | - "name": "GitLab dev user", | ||
| 34 | - "email": "gitlabdev@dv6700.(none)", | ||
| 35 | - }, | ||
| 36 | - }, | ||
| 37 | - ], | ||
| 38 | - "total_commits_count": 4, | ||
| 39 | -}; | ||
| 40 | -eos | ||
| 41 | -%> | ||
| 42 | -<div class="<%= user_color_scheme_class%>"> | ||
| 43 | - <%= raw Pygments::Lexer[:js].highlight(data_ex_str) %> | ||
| 44 | -</div> |
app/views/projects/hooks/index.html.haml
| 1 | %h3.page-title | 1 | %h3.page-title |
| 2 | - Post-receive hooks | 2 | + Web hooks |
| 3 | 3 | ||
| 4 | %p.light | 4 | %p.light |
| 5 | - #{link_to "Post-receive hooks ", help_web_hooks_path, class: "vlink"} can be | ||
| 6 | - used for binding events when someone pushes to the repository. | 5 | + #{link_to "Web hooks ", help_web_hooks_path, class: "vlink"} can be |
| 6 | + used for binding events when something happends to the the project. | ||
| 7 | 7 | ||
| 8 | %hr.clearfix | 8 | %hr.clearfix |
| 9 | 9 | ||
| @@ -13,23 +13,50 @@ | @@ -13,23 +13,50 @@ | ||
| 13 | - @hook.errors.full_messages.each do |msg| | 13 | - @hook.errors.full_messages.each do |msg| |
| 14 | %p= msg | 14 | %p= msg |
| 15 | .control-group | 15 | .control-group |
| 16 | - = f.label :url, "URL:" | 16 | + = f.label :url, "URL" |
| 17 | .controls | 17 | .controls |
| 18 | = f.text_field :url, class: "text_field input-xxlarge input-xpadding", placeholder: 'http://example.com/trigger-ci.json' | 18 | = f.text_field :url, class: "text_field input-xxlarge input-xpadding", placeholder: 'http://example.com/trigger-ci.json' |
| 19 | | 19 | |
| 20 | = f.submit "Add Web Hook", class: "btn btn-create" | 20 | = f.submit "Add Web Hook", class: "btn btn-create" |
| 21 | + .control-group | ||
| 22 | + = f.label :url, "Trigger" | ||
| 23 | + .controls | ||
| 24 | + %div | ||
| 25 | + = f.check_box :push_events, class: 'pull-left' | ||
| 26 | + .prepend-left-20 | ||
| 27 | + = f.label :push_events, class: 'list-label' do | ||
| 28 | + %strong Push events | ||
| 29 | + %p.light | ||
| 30 | + This url will be triggered in case of push to repository | ||
| 31 | + %div | ||
| 32 | + = f.check_box :issues_events, class: 'pull-left' | ||
| 33 | + .prepend-left-20 | ||
| 34 | + = f.label :issues_events, class: 'list-label' do | ||
| 35 | + %strong Issues events | ||
| 36 | + %p.light | ||
| 37 | + This url will be triggered for created issues | ||
| 38 | + %div | ||
| 39 | + = f.check_box :merge_requests_events, class: 'pull-left' | ||
| 40 | + .prepend-left-20 | ||
| 41 | + = f.label :merge_requests_events, class: 'list-label' do | ||
| 42 | + %strong Merge Request events | ||
| 43 | + %p.light | ||
| 44 | + This url will be triggered for created merge requests | ||
| 21 | %hr | 45 | %hr |
| 22 | 46 | ||
| 23 | -if @hooks.any? | 47 | -if @hooks.any? |
| 24 | .ui-box | 48 | .ui-box |
| 25 | .title | 49 | .title |
| 26 | - Hooks (#{@hooks.count}) | 50 | + Web Hooks (#{@hooks.count}) |
| 27 | %ul.well-list | 51 | %ul.well-list |
| 28 | - @hooks.each do |hook| | 52 | - @hooks.each do |hook| |
| 29 | %li | 53 | %li |
| 30 | - %span.badge.badge-info POST | ||
| 31 | - → | ||
| 32 | - %span.monospace= hook.url | ||
| 33 | .pull-right | 54 | .pull-right |
| 34 | = link_to 'Test Hook', test_project_hook_path(@project, hook), class: "btn btn-small grouped" | 55 | = link_to 'Test Hook', test_project_hook_path(@project, hook), class: "btn btn-small grouped" |
| 35 | = link_to 'Remove', project_hook_path(@project, hook), confirm: 'Are you sure?', method: :delete, class: "btn btn-remove btn-small grouped" | 56 | = link_to 'Remove', project_hook_path(@project, hook), confirm: 'Are you sure?', method: :delete, class: "btn btn-remove btn-small grouped" |
| 57 | + .clearfix | ||
| 58 | + %span.monospace= hook.url | ||
| 59 | + %p | ||
| 60 | + - %w(push_events issues_events merge_requests_events).each do |trigger| | ||
| 61 | + - if hook.send(trigger) | ||
| 62 | + %span.label.label-gray= trigger.titleize |
app/views/projects/issues/_head.html.haml
| 1 | %ul.nav.nav-tabs | 1 | %ul.nav.nav-tabs |
| 2 | = nav_link(controller: :issues) do | 2 | = nav_link(controller: :issues) do |
| 3 | - = link_to 'Browse Issues', project_issues_path(@project), class: "tab" | 3 | + = link_to project_issues_path(@project), class: "tab" do |
| 4 | + Browse Issues | ||
| 5 | + - if current_controller?(:issues) | ||
| 6 | + %span.badge.issue_counter #{@issues.total_count} | ||
| 4 | = nav_link(controller: :milestones) do | 7 | = nav_link(controller: :milestones) do |
| 5 | = link_to 'Milestones', project_milestones_path(@project), class: "tab" | 8 | = link_to 'Milestones', project_milestones_path(@project), class: "tab" |
| 6 | = nav_link(controller: :labels) do | 9 | = nav_link(controller: :labels) do |
| 7 | = link_to 'Labels', project_labels_path(@project), class: "tab" | 10 | = link_to 'Labels', project_labels_path(@project), class: "tab" |
| 8 | - - if current_user | 11 | + |
| 12 | + - if current_controller?(:issues) | ||
| 13 | + - if current_user | ||
| 14 | + %li | ||
| 15 | + = link_to project_issues_path(@project, :atom, { private_token: current_user.private_token }) do | ||
| 16 | + %i.icon-rss | ||
| 17 | + | ||
| 9 | %li.pull-right | 18 | %li.pull-right |
| 10 | - = link_to project_issues_path(@project, :atom, { private_token: current_user.private_token }) do | ||
| 11 | - %i.icon-rss | 19 | + .pull-right |
| 20 | + - if can? current_user, :write_issue, @project | ||
| 21 | + = link_to new_project_issue_path(@project, issue: { assignee_id: params[:assignee_id], milestone_id: params[:milestone_id]}), class: "btn btn-new pull-right", title: "New Issue", id: "new_issue_link" do | ||
| 22 | + %i.icon-plus | ||
| 23 | + New Issue | ||
| 24 | + = form_tag project_issues_path(@project), method: :get, id: "issue_search_form", class: 'pull-right issue-search-form' do | ||
| 25 | + = search_field_tag :issue_search, nil, { placeholder: 'Filter by title or description', class: 'input-xpadding issue_search input-xlarge append-right-10 search-text-input' } |
app/views/projects/issues/_issues.html.haml
| @@ -6,8 +6,8 @@ | @@ -6,8 +6,8 @@ | ||
| 6 | = form_tag bulk_update_project_issues_path(@project), method: :post do | 6 | = form_tag bulk_update_project_issues_path(@project), method: :post do |
| 7 | %span Update selected issues with | 7 | %span Update selected issues with |
| 8 | = select_tag('update[status]', options_for_select(['open', 'closed']), prompt: "Status") | 8 | = select_tag('update[status]', options_for_select(['open', 'closed']), prompt: "Status") |
| 9 | - = select_tag('update[assignee_id]', options_from_collection_for_select(@project.team.members, "id", "name", params[:assignee_id]), prompt: "Assignee") | ||
| 10 | - = select_tag('update[milestone_id]', options_from_collection_for_select(project_active_milestones, "id", "title", params[:milestone_id]), prompt: "Milestone") | 9 | + = select_tag('update[assignee_id]', bulk_update_assignee_options, prompt: "Assignee") |
| 10 | + = select_tag('update[milestone_id]', bulk_update_milestone_options, prompt: "Milestone") | ||
| 11 | = hidden_field_tag 'update[issues_ids]', [] | 11 | = hidden_field_tag 'update[issues_ids]', [] |
| 12 | = hidden_field_tag :status, params[:status] | 12 | = hidden_field_tag :status, params[:status] |
| 13 | = button_tag "Save", class: "btn update_selected_issues btn-small btn-save" | 13 | = button_tag "Save", class: "btn update_selected_issues btn-small btn-save" |
| @@ -78,6 +78,29 @@ | @@ -78,6 +78,29 @@ | ||
| 78 | %strong= milestone.title | 78 | %strong= milestone.title |
| 79 | %small.light= milestone.expires_at | 79 | %small.light= milestone.expires_at |
| 80 | 80 | ||
| 81 | + .dropdown.inline.prepend-left-10 | ||
| 82 | + %a.dropdown-toggle.btn.btn-small{href: '#', "data-toggle" => "dropdown"} | ||
| 83 | + %span.light sort: | ||
| 84 | + - if @sort.present? | ||
| 85 | + = @sort | ||
| 86 | + - else | ||
| 87 | + Newest | ||
| 88 | + %b.caret | ||
| 89 | + %ul.dropdown-menu | ||
| 90 | + %li | ||
| 91 | + = link_to project_filter_path(sort: 'newest') do | ||
| 92 | + Newest | ||
| 93 | + = link_to project_filter_path(sort: 'oldest') do | ||
| 94 | + Oldest | ||
| 95 | + = link_to project_filter_path(sort: 'recently_updated') do | ||
| 96 | + Recently updated | ||
| 97 | + = link_to project_filter_path(sort: 'last_updated') do | ||
| 98 | + Last updated | ||
| 99 | + = link_to project_filter_path(sort: 'milestone_due_soon') do | ||
| 100 | + Milestone due soon | ||
| 101 | + = link_to project_filter_path(sort: 'milestone_due_later') do | ||
| 102 | + Milestone due later | ||
| 103 | + | ||
| 81 | 104 | ||
| 82 | %ul.well-list.issues-list | 105 | %ul.well-list.issues-list |
| 83 | = render @issues | 106 | = render @issues |
| @@ -90,4 +113,4 @@ | @@ -90,4 +113,4 @@ | ||
| 90 | %span.issue_counter #{@issues.total_count} | 113 | %span.issue_counter #{@issues.total_count} |
| 91 | issues for this filter | 114 | issues for this filter |
| 92 | 115 | ||
| 93 | - = paginate @issues, remote: true, theme: "gitlab" | 116 | + = paginate @issues, theme: "gitlab" |
app/views/projects/issues/index.html.haml
| 1 | = render "head" | 1 | = render "head" |
| 2 | -.issues_content | ||
| 3 | - %h3.page-title | ||
| 4 | - Issues | ||
| 5 | - %span (<span class=issue_counter>#{@issues.total_count}</span>) | ||
| 6 | - .pull-right | ||
| 7 | - .span6 | ||
| 8 | - - if can? current_user, :write_issue, @project | ||
| 9 | - = link_to new_project_issue_path(@project, issue: { assignee_id: params[:assignee_id], milestone_id: params[:milestone_id]}), class: "btn btn-new pull-right", title: "New Issue", id: "new_issue_link" do | ||
| 10 | - %i.icon-plus | ||
| 11 | - New Issue | ||
| 12 | - = form_tag project_issues_path(@project), method: :get, remote: true, id: "issue_search_form", class: 'pull-right' do | ||
| 13 | - = hidden_field_tag :status, params[:status], id: 'search_status' | ||
| 14 | - = hidden_field_tag :assignee_id, params[:assignee_id], id: 'search_assignee_id' | ||
| 15 | - = hidden_field_tag :milestone_id, params[:milestone_id], id: 'search_milestone_id' | ||
| 16 | - = hidden_field_tag :label_name, params[:label_name], id: 'search_label_name' | ||
| 17 | - = search_field_tag :issue_search, nil, { placeholder: 'Filter by title or description', class: 'input-xpadding issue_search input-xlarge append-right-10 search-text-input' } | ||
| 18 | - | ||
| 19 | .row | 2 | .row |
| 20 | .span3 | 3 | .span3 |
| 21 | = render 'shared/project_filter', project_entities_path: project_issues_path(@project) | 4 | = render 'shared/project_filter', project_entities_path: project_issues_path(@project) |
app/views/projects/issues/index.js.haml
app/views/projects/issues/show.html.haml
| @@ -33,6 +33,8 @@ | @@ -33,6 +33,8 @@ | ||
| 33 | %h4.box-title | 33 | %h4.box-title |
| 34 | - if @issue.closed? | 34 | - if @issue.closed? |
| 35 | .state-label.state-label-red Closed | 35 | .state-label.state-label-red Closed |
| 36 | + - else | ||
| 37 | + .state-label.state-label-green Open | ||
| 36 | = gfm escape_once(@issue.title) | 38 | = gfm escape_once(@issue.title) |
| 37 | 39 | ||
| 38 | .ui-box-body | 40 | .ui-box-body |
| @@ -71,4 +73,4 @@ | @@ -71,4 +73,4 @@ | ||
| 71 | - @issue.participants.each do |participant| | 73 | - @issue.participants.each do |participant| |
| 72 | = link_to_member(@project, participant, name: false, size: 24) | 74 | = link_to_member(@project, participant, name: false, size: 24) |
| 73 | 75 | ||
| 74 | -.voting_notes#notes= render "projects/notes/notes_with_form" | 76 | -.voting_notes#notes= render "projects/notes/notes_with_form" |
| 77 | +.voting_notes#notes= render "projects/notes/notes_with_form" | ||
| 75 | \ No newline at end of file | 78 | \ No newline at end of file |
app/views/projects/merge_requests/_form.html.haml
| @@ -13,7 +13,6 @@ | @@ -13,7 +13,6 @@ | ||
| 13 | = f.select(:source_project_id,[[@merge_request.source_project.path_with_namespace,@merge_request.source_project.id]] , {}, {class: 'source_project chosen span3'}) | 13 | = f.select(:source_project_id,[[@merge_request.source_project.path_with_namespace,@merge_request.source_project.id]] , {}, {class: 'source_project chosen span3'}) |
| 14 | .pull-left | 14 | .pull-left |
| 15 | | 15 | |
| 16 | - %i.icon-code-fork | ||
| 17 | = f.select(:source_branch, @merge_request.source_project.repository.branch_names, { include_blank: "Select branch" }, {class: 'source_branch chosen span2'}) | 16 | = f.select(:source_branch, @merge_request.source_project.repository.branch_names, { include_blank: "Select branch" }, {class: 'source_branch chosen span2'}) |
| 18 | .mr_source_commit.prepend-top-10 | 17 | .mr_source_commit.prepend-top-10 |
| 19 | .span2 | 18 | .span2 |
| @@ -26,7 +25,6 @@ | @@ -26,7 +25,6 @@ | ||
| 26 | = f.select(:target_project_id, projects.map { |proj| [proj.path_with_namespace,proj.id] }, {include_blank: "Select Target Project" }, {class: 'target_project chosen span3'}) | 25 | = f.select(:target_project_id, projects.map { |proj| [proj.path_with_namespace,proj.id] }, {include_blank: "Select Target Project" }, {class: 'target_project chosen span3'}) |
| 27 | .pull-left | 26 | .pull-left |
| 28 | | 27 | |
| 29 | - %i.icon-code-fork | ||
| 30 | = f.select(:target_branch, @target_branches, { include_blank: "Select branch" }, {class: 'target_branch chosen span2'}) | 28 | = f.select(:target_branch, @target_branches, { include_blank: "Select branch" }, {class: 'target_branch chosen span2'}) |
| 31 | .mr_target_commit.prepend-top-10 | 29 | .mr_target_commit.prepend-top-10 |
| 32 | 30 |
app/views/projects/merge_requests/commits.js.haml
app/views/projects/merge_requests/diffs.js.haml
app/views/projects/merge_requests/show.js.haml
app/views/projects/merge_requests/show/_mr_box.html.haml
| @@ -13,13 +13,14 @@ | @@ -13,13 +13,14 @@ | ||
| 13 | .ui-box-body | 13 | .ui-box-body |
| 14 | %div | 14 | %div |
| 15 | %cite.cgray | 15 | %cite.cgray |
| 16 | - Created on #{@merge_request.created_at.stamp("Aug 21, 2011")} by #{link_to_member(@project, @merge_request.author)} | 16 | + Created on #{@merge_request.created_at.stamp("Aug 21, 2011")} by #{link_to_member(@project, @merge_request.author)}. |
| 17 | - if @merge_request.assignee | 17 | - if @merge_request.assignee |
| 18 | - \, currently assigned to #{link_to_member(@project, @merge_request.assignee)} | 18 | + Currently assigned to #{link_to_member(@project, @merge_request.assignee)}. |
| 19 | - if @merge_request.milestone | 19 | - if @merge_request.milestone |
| 20 | - milestone = @merge_request.milestone | 20 | - milestone = @merge_request.milestone |
| 21 | - %cite.cgray and attached to milestone | 21 | + %cite.cgray Attached to milestone |
| 22 | %strong= link_to_gfm truncate(milestone.title, length: 20), project_milestone_path(milestone.project, milestone) | 22 | %strong= link_to_gfm truncate(milestone.title, length: 20), project_milestone_path(milestone.project, milestone) |
| 23 | + \. | ||
| 23 | 24 | ||
| 24 | 25 | ||
| 25 | - if @merge_request.description.present? | 26 | - if @merge_request.description.present? |
app/views/projects/new.html.haml
| @@ -47,12 +47,7 @@ | @@ -47,12 +47,7 @@ | ||
| 47 | %span.light (optional) | 47 | %span.light (optional) |
| 48 | .controls | 48 | .controls |
| 49 | = f.text_area :description, placeholder: "Awesome project", class: "input-xlarge", rows: 3, maxlength: 250, tabindex: 3 | 49 | = f.text_area :description, placeholder: "Awesome project", class: "input-xlarge", rows: 3, maxlength: 250, tabindex: 3 |
| 50 | - .control-group.project-public-holder | ||
| 51 | - = f.label :public do | ||
| 52 | - %span Public project | ||
| 53 | - .controls | ||
| 54 | - = f.check_box :public, { checked: gitlab_config.default_projects_features.public }, true, false | ||
| 55 | - %span.help-inline Make project visible to everyone | 50 | + = render "visibility_level", f: f, visibility_level: gitlab_config.default_projects_features.visibility_level, can_change_visibility_level: true |
| 56 | 51 | ||
| 57 | .form-actions | 52 | .form-actions |
| 58 | = f.submit 'Create project', class: "btn btn-create project-submit", tabindex: 4 | 53 | = f.submit 'Create project', class: "btn btn-create project-submit", tabindex: 4 |
app/views/projects/notes/_diff_notes_with_reply_parallel.html.haml
0 → 100644
| @@ -0,0 +1,34 @@ | @@ -0,0 +1,34 @@ | ||
| 1 | +- note1 = notes1.first # example note | ||
| 2 | +- note2 = notes2.first # example note | ||
| 3 | +%tr.notes_holder | ||
| 4 | + -# Check if line want not changed since comment was left | ||
| 5 | + /- if !defined?(line1) || line1 == note1.diff_line | ||
| 6 | + - if note1 | ||
| 7 | + %td.notes_content | ||
| 8 | + %ul.notes{ rel: note1.discussion_id } | ||
| 9 | + = render notes1 | ||
| 10 | + = render "projects/notes/discussion_reply_button", note: note1 | ||
| 11 | + %td.notes_line2 | ||
| 12 | + %span.btn.disabled.parallel-comment | ||
| 13 | + %i.icon-comment | ||
| 14 | + = notes1.count | ||
| 15 | + - else | ||
| 16 | + %td= "" | ||
| 17 | + %td= "" | ||
| 18 | + | ||
| 19 | + %td= "" | ||
| 20 | + | ||
| 21 | + -# Check if line want not changed since comment was left | ||
| 22 | + /- if !defined?(line2) || line2 == note2.diff_line | ||
| 23 | + - if note2 | ||
| 24 | + %td.notes_line | ||
| 25 | + %span.btn.disabled.parallel-comment | ||
| 26 | + %i.icon-comment | ||
| 27 | + = notes2.count | ||
| 28 | + %td.notes_content | ||
| 29 | + %ul.notes{ rel: note2.discussion_id } | ||
| 30 | + = render notes2 | ||
| 31 | + = render "projects/notes/discussion_reply_button", note: note2 | ||
| 32 | + - else | ||
| 33 | + %td= "" | ||
| 34 | + %td= "" |
app/views/projects/notes/_form.html.haml
| @@ -7,10 +7,12 @@ | @@ -7,10 +7,12 @@ | ||
| 7 | = f.hidden_field :noteable_type | 7 | = f.hidden_field :noteable_type |
| 8 | 8 | ||
| 9 | .note_text_and_preview.js-toggler-container | 9 | .note_text_and_preview.js-toggler-container |
| 10 | - %a.js-note-preview-button.js-toggler-target.turn-off{ href: "javascript:;", title: "Preview", data: {url: preview_project_notes_path(@project)} } | 10 | + %a.btn.btn-primary.js-note-preview-button.js-toggler-target.turn-off{ href: "javascript:;", data: {url: preview_project_notes_path(@project)} } |
| 11 | %i.icon-eye-open | 11 | %i.icon-eye-open |
| 12 | - %a.js-note-edit-button.js-toggler-target.turn-off{ href: "javascript:;", title: "Edit" } | 12 | + Preview |
| 13 | + %a.btn.btn-primary.js-note-edit-button.js-toggler-target.turn-off{ href: "javascript:;" } | ||
| 13 | %i.icon-edit | 14 | %i.icon-edit |
| 15 | + Write | ||
| 14 | 16 | ||
| 15 | = f.text_area :note, size: 255, class: 'note_text js-note-text js-gfm-input turn-on' | 17 | = f.text_area :note, size: 255, class: 'note_text js-note-text js-gfm-input turn-on' |
| 16 | .note_preview.js-note-preview.turn-off | 18 | .note_preview.js-note-preview.turn-off |
| @@ -27,7 +29,7 @@ | @@ -27,7 +29,7 @@ | ||
| 27 | %a.btn.grouped.js-close-discussion-note-form Cancel | 29 | %a.btn.grouped.js-close-discussion-note-form Cancel |
| 28 | 30 | ||
| 29 | .note-form-option | 31 | .note-form-option |
| 30 | - %a.choose-btn.btn.btn-small.js-choose-note-attachment-button | 32 | + %a.choose-btn.btn.js-choose-note-attachment-button |
| 31 | %i.icon-paper-clip | 33 | %i.icon-paper-clip |
| 32 | %span Choose File ... | 34 | %span Choose File ... |
| 33 | | 35 | |
app/views/projects/notes/index.js.haml
app/views/projects/services/_form.html.haml
app/views/projects/services/index.html.haml
| @@ -6,12 +6,8 @@ | @@ -6,12 +6,8 @@ | ||
| 6 | - @services.each do |service| | 6 | - @services.each do |service| |
| 7 | %li | 7 | %li |
| 8 | %h4 | 8 | %h4 |
| 9 | - - if service.activated? | ||
| 10 | - %span.cgreen | ||
| 11 | - %i.icon-circle | ||
| 12 | - - else | ||
| 13 | - %span.cgray | ||
| 14 | - %i.icon-circle-blank | ||
| 15 | = link_to edit_project_service_path(@project, service.to_param) do | 9 | = link_to edit_project_service_path(@project, service.to_param) do |
| 16 | = service.title | 10 | = service.title |
| 11 | + .pull-right | ||
| 12 | + = boolean_to_icon service.activated? | ||
| 17 | %p= service.description | 13 | %p= service.description |
app/views/projects/show.html.haml
| 1 | -.project-home-panel | ||
| 2 | - .row | ||
| 3 | - .span4 | ||
| 4 | - %h4.project-home-title | ||
| 5 | - = @project.name_with_namespace | ||
| 6 | - - if @project.public | ||
| 7 | - %span.public-label Public | ||
| 8 | - - else | ||
| 9 | - %span.public-label Private | ||
| 10 | - | ||
| 11 | - .span8 | ||
| 12 | - .project-home-dropdown | ||
| 13 | - = render "dropdown" | ||
| 14 | - .form-horizontal | ||
| 15 | - = render "shared/clone_panel" | ||
| 16 | - | ||
| 17 | - .project-home-extra.clearfix | ||
| 18 | - .project-home-desc | ||
| 19 | - - if @project.description.present? | ||
| 20 | - = @project.description | ||
| 21 | - - if can?(current_user, :admin_project, @project) | ||
| 22 | - – | ||
| 23 | - %strong= link_to 'Edit', edit_project_path | ||
| 24 | - | ||
| 25 | - .project-home-links | ||
| 26 | - = link_to pluralize(@repository.round_commit_count, 'commit'), project_commits_path(@project, @ref || @repository.root_ref) | ||
| 27 | - = link_to pluralize(@repository.branch_names.count, 'branch'), project_branches_path(@project) | ||
| 28 | - = link_to pluralize(@repository.tag_names.count, 'tag'), project_tags_path(@project) | ||
| 29 | - %span.light.prepend-left-20= repository_size | 1 | += render "home_panel" |
| 30 | 2 | ||
| 31 | .row | 3 | .row |
| 32 | .span9 | 4 | .span9 |
| @@ -34,19 +6,20 @@ | @@ -34,19 +6,20 @@ | ||
| 34 | = render 'shared/event_filter' | 6 | = render 'shared/event_filter' |
| 35 | .content_list | 7 | .content_list |
| 36 | .loading.hide | 8 | .loading.hide |
| 37 | - .span3 | 9 | + .span3.project-side |
| 38 | .clearfix | 10 | .clearfix |
| 39 | - if @project.forked_from_project | 11 | - if @project.forked_from_project |
| 40 | .alert.alert-success | 12 | .alert.alert-success |
| 41 | - %i.icon-code-fork | 13 | + %i.icon-code-fork.project-fork-icon |
| 42 | Forked from: | 14 | Forked from: |
| 15 | + %br | ||
| 43 | = link_to @project.forked_from_project.name_with_namespace, project_path(@project.forked_from_project) | 16 | = link_to @project.forked_from_project.name_with_namespace, project_path(@project.forked_from_project) |
| 44 | - unless @project.empty_repo? | 17 | - unless @project.empty_repo? |
| 45 | - if current_user && can?(current_user, :fork_project, @project) && @project.namespace != current_user.namespace | 18 | - if current_user && can?(current_user, :fork_project, @project) && @project.namespace != current_user.namespace |
| 46 | - if current_user.already_forked?(@project) | 19 | - if current_user.already_forked?(@project) |
| 47 | = link_to project_path(current_user.fork_of(@project)), class: 'btn btn-block' do | 20 | = link_to project_path(current_user.fork_of(@project)), class: 'btn btn-block' do |
| 48 | - %i.icon-ok | ||
| 49 | - Already forked | 21 | + %i.icon-compass |
| 22 | + Go to fork | ||
| 50 | - else | 23 | - else |
| 51 | = link_to fork_project_path(@project), title: "Fork", class: "btn btn-block", method: "POST" do | 24 | = link_to fork_project_path(@project), title: "Fork", class: "btn btn-block", method: "POST" do |
| 52 | %i.icon-code-fork | 25 | %i.icon-code-fork |
| @@ -56,8 +29,15 @@ | @@ -56,8 +29,15 @@ | ||
| 56 | = link_to archive_project_repository_path(@project), class: "btn btn-block" do | 29 | = link_to archive_project_repository_path(@project), class: "btn btn-block" do |
| 57 | %i.icon-download-alt | 30 | %i.icon-download-alt |
| 58 | %span Download | 31 | %span Download |
| 59 | - %br | ||
| 60 | - .light-well | 32 | + = link_to project_compare_index_path(@project, from: @repository.root_ref, to: @ref || @repository.root_ref), class: 'btn btn-block' do |
| 33 | + Compare code | ||
| 34 | + | ||
| 35 | + - if @repository.readme | ||
| 36 | + - readme = @repository.readme | ||
| 37 | + = link_to project_blob_path(@project, tree_join(@repository.root_ref, readme.name)), class: 'btn btn-block' do | ||
| 38 | + = readme.name | ||
| 39 | + | ||
| 40 | + .prepend-top-10 | ||
| 61 | %p | 41 | %p |
| 62 | %span.light Created on | 42 | %span.light Created on |
| 63 | #{@project.created_at.stamp('Aug 22, 2013')} | 43 | #{@project.created_at.stamp('Aug 22, 2013')} |
app/views/projects/show.js.haml
app/views/public/projects/index.html.haml
| @@ -19,6 +19,10 @@ | @@ -19,6 +19,10 @@ | ||
| 19 | %h4 | 19 | %h4 |
| 20 | = link_to project_path(project) do | 20 | = link_to project_path(project) do |
| 21 | = project.name_with_namespace | 21 | = project.name_with_namespace |
| 22 | + - if project.internal? | ||
| 23 | + %small.access-icon | ||
| 24 | + = internal_icon | ||
| 25 | + Internal | ||
| 22 | .pull-right | 26 | .pull-right |
| 23 | %pre.public-clone git clone #{project.http_url_to_repo} | 27 | %pre.public-clone git clone #{project.http_url_to_repo} |
| 24 | 28 |
app/views/shared/_clone_panel.html.haml
| 1 | .git-clone-holder | 1 | .git-clone-holder |
| 2 | - %button{class: "btn active", :"data-clone" => @project.ssh_url_to_repo} SSH | ||
| 3 | - %button{class: "btn", :"data-clone" => @project.http_url_to_repo}= gitlab_config.protocol.upcase | ||
| 4 | - = text_field_tag :project_clone, @project.url_to_repo, class: "one_click_select span5", readonly: true | 2 | + %button{class: "btn #{ current_user ? 'active' : '' }", :"data-clone" => @project.ssh_url_to_repo} SSH |
| 3 | + %button{class: "btn #{ current_user ? '' : 'active' }", :"data-clone" => @project.http_url_to_repo}= gitlab_config.protocol.upcase | ||
| 4 | + = text_field_tag :project_clone, (current_user ? @project.url_to_repo : @project.http_url_to_repo), class: "one_click_select span5", readonly: true |
app/views/shared/_merge_requests.html.haml
| @@ -4,6 +4,7 @@ | @@ -4,6 +4,7 @@ | ||
| 4 | - project = group[0] | 4 | - project = group[0] |
| 5 | .title | 5 | .title |
| 6 | = link_to_project project | 6 | = link_to_project project |
| 7 | + = link_to 'show all', project_merge_requests_path(project), class: 'pull-right' | ||
| 7 | %ul.well-list.mr-list | 8 | %ul.well-list.mr-list |
| 8 | - group[1].each do |merge_request| | 9 | - group[1].each do |merge_request| |
| 9 | = render 'projects/merge_requests/merge_request', merge_request: merge_request | 10 | = render 'projects/merge_requests/merge_request', merge_request: merge_request |
app/views/shared/_no_ssh.html.haml
| 1 | -- if current_user.require_ssh_key? && alert.blank? && notice.blank? | ||
| 2 | - %p.error-message.centered | ||
| 3 | - 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 | 1 | +- if cookies[:hide_no_ssh_message].blank? && current_user.require_ssh_key? |
| 2 | + .no-ssh-key-message | ||
| 3 | + .container | ||
| 4 | + 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 | ||
| 5 | + = link_to '#', class: 'pull-right hide-no-ssh-message' do | ||
| 6 | + %i.icon-remove |
app/views/snippets/_blob.html.haml
| @@ -8,9 +8,18 @@ | @@ -8,9 +8,18 @@ | ||
| 8 | = link_to "Edit", edit_snippet_path(@snippet), class: "btn btn-tiny", title: 'Edit Snippet' | 8 | = link_to "Edit", edit_snippet_path(@snippet), class: "btn btn-tiny", title: 'Edit Snippet' |
| 9 | = link_to "Delete", snippet_path(@snippet), method: :delete, confirm: "Are you sure?", class: "btn btn-tiny", title: 'Delete Snippet' | 9 | = link_to "Delete", snippet_path(@snippet), method: :delete, confirm: "Are you sure?", class: "btn btn-tiny", title: 'Delete Snippet' |
| 10 | = link_to "Raw", raw_snippet_path(@snippet), class: "btn btn-tiny", target: "_blank" | 10 | = link_to "Raw", raw_snippet_path(@snippet), class: "btn btn-tiny", target: "_blank" |
| 11 | - .file-content.code | ||
| 12 | - - unless @snippet.content.empty? | ||
| 13 | - %div{class: user_color_scheme_class} | ||
| 14 | - = raw @snippet.colorize(formatter: :gitlab) | 11 | + - unless @snippet.content.empty? |
| 12 | + - if gitlab_markdown?(@snippet.file_name) | ||
| 13 | + .file-content.wiki | ||
| 14 | + = preserve do | ||
| 15 | + = markdown(@snippet.data) | ||
| 16 | + - elsif markup?(@snippet.file_name) | ||
| 17 | + .file-content.wiki | ||
| 18 | + = raw GitHub::Markup.render(@snippet.file_name, @snippet.data) | ||
| 15 | - else | 19 | - else |
| 20 | + .file-content.code | ||
| 21 | + %div{class: user_color_scheme_class} | ||
| 22 | + = raw @snippet.colorize(formatter: :gitlab) | ||
| 23 | + - else | ||
| 24 | + .file-content.code | ||
| 16 | %p.nothing_here_message Empty file | 25 | %p.nothing_here_message Empty file |
app/views/snippets/_form.html.haml
| @@ -13,9 +13,20 @@ | @@ -13,9 +13,20 @@ | ||
| 13 | = f.label :title | 13 | = f.label :title |
| 14 | .controls= f.text_field :title, placeholder: "Example Snippet", class: 'input-xlarge', required: true | 14 | .controls= f.text_field :title, placeholder: "Example Snippet", class: 'input-xlarge', required: true |
| 15 | .control-group | 15 | .control-group |
| 16 | - = f.label "Private?" | 16 | + = f.label "Access" |
| 17 | .controls | 17 | .controls |
| 18 | - = f.check_box :private, {class: ''} | 18 | + = f.label :private_true, class: 'radio-label' do |
| 19 | + = f.radio_button :private, true | ||
| 20 | + %span | ||
| 21 | + %strong Private | ||
| 22 | + (only you can see this snippet) | ||
| 23 | + %br | ||
| 24 | + = f.label :private_false, class: 'radio-label' do | ||
| 25 | + = f.radio_button :private, false | ||
| 26 | + %span | ||
| 27 | + %strong Public | ||
| 28 | + (GitLab users can can see this snippet) | ||
| 29 | + | ||
| 19 | .control-group | 30 | .control-group |
| 20 | .file-editor | 31 | .file-editor |
| 21 | = f.label :file_name, "File" | 32 | = f.label :file_name, "File" |
| @@ -33,9 +44,10 @@ | @@ -33,9 +44,10 @@ | ||
| 33 | - else | 44 | - else |
| 34 | = f.submit 'Save', class: "btn-save btn" | 45 | = f.submit 'Save', class: "btn-save btn" |
| 35 | 46 | ||
| 36 | - = link_to "Cancel", snippets_path(@project), class: "btn btn-cancel" | ||
| 37 | - unless @snippet.new_record? | 47 | - unless @snippet.new_record? |
| 38 | - .pull-right= link_to 'Destroy', snippet_path(@snippet), confirm: 'Removed snippet cannot be restored! Are you sure?', method: :delete, class: "btn pull-right danger delete-snippet", id: "destroy_snippet_#{@snippet.id}" | 48 | + .pull-right.prepend-left-20 |
| 49 | + = link_to 'Remove', snippet_path(@snippet), confirm: 'Removed snippet cannot be restored! Are you sure?', method: :delete, class: "btn btn-remove delete-snippet", id: "destroy_snippet_#{@snippet.id}" | ||
| 50 | + = link_to "Cancel", snippets_path(@project), class: "btn btn-cancel" | ||
| 39 | 51 | ||
| 40 | 52 | ||
| 41 | :javascript | 53 | :javascript |
app/views/snippets/_snippet.html.haml
| @@ -3,7 +3,7 @@ | @@ -3,7 +3,7 @@ | ||
| 3 | = link_to reliable_snippet_path(snippet) do | 3 | = link_to reliable_snippet_path(snippet) do |
| 4 | = truncate(snippet.title, length: 60) | 4 | = truncate(snippet.title, length: 60) |
| 5 | - if snippet.private? | 5 | - if snippet.private? |
| 6 | - %span.label.label-success | 6 | + %span.label.label-gray |
| 7 | %i.icon-lock | 7 | %i.icon-lock |
| 8 | private | 8 | private |
| 9 | %span.cgray.monospace.tiny.pull-right | 9 | %span.cgray.monospace.tiny.pull-right |
app/workers/repository_import_worker.rb
| @@ -14,7 +14,6 @@ class RepositoryImportWorker | @@ -14,7 +14,6 @@ class RepositoryImportWorker | ||
| 14 | project.imported = true | 14 | project.imported = true |
| 15 | project.save | 15 | project.save |
| 16 | project.satellite.create unless project.satellite.exists? | 16 | project.satellite.create unless project.satellite.exists? |
| 17 | - project.discover_default_branch | ||
| 18 | else | 17 | else |
| 19 | project.imported = false | 18 | project.imported = false |
| 20 | end | 19 | end |
config/application.rb
| @@ -70,7 +70,7 @@ module Gitlab | @@ -70,7 +70,7 @@ module Gitlab | ||
| 70 | config.assets.version = '1.0' | 70 | config.assets.version = '1.0' |
| 71 | 71 | ||
| 72 | # Uncomment and customize the last line to run in a non-root path | 72 | # Uncomment and customize the last line to run in a non-root path |
| 73 | - # WARNING: This feature is known to work, but unsupported | 73 | + # WARNING: We recommend creating a FQDN to host GitLab in a root path instead of this. |
| 74 | # Note that four settings need to be changed for this to work. | 74 | # Note that four settings need to be changed for this to work. |
| 75 | # 1) In your application.rb file: config.relative_url_root = "/gitlab" | 75 | # 1) In your application.rb file: config.relative_url_root = "/gitlab" |
| 76 | # 2) In your gitlab.yml file: relative_url_root: /gitlab | 76 | # 2) In your gitlab.yml file: relative_url_root: /gitlab |
| @@ -80,7 +80,14 @@ module Gitlab | @@ -80,7 +80,14 @@ module Gitlab | ||
| 80 | # | 80 | # |
| 81 | # config.relative_url_root = "/gitlab" | 81 | # config.relative_url_root = "/gitlab" |
| 82 | 82 | ||
| 83 | - # Uncomment to enable rack attack middleware | ||
| 84 | - # config.middleware.use Rack::Attack | 83 | + config.middleware.use Rack::Attack |
| 84 | + | ||
| 85 | + # Allow access to GitLab API from other domains | ||
| 86 | + config.middleware.use Rack::Cors do | ||
| 87 | + allow do | ||
| 88 | + origins '*' | ||
| 89 | + resource '/api/*', headers: :any, methods: [:get, :post, :options, :put] | ||
| 90 | + end | ||
| 91 | + end | ||
| 85 | end | 92 | end |
| 86 | end | 93 | end |
config/database.yml.mysql
| @@ -7,7 +7,7 @@ production: | @@ -7,7 +7,7 @@ production: | ||
| 7 | reconnect: false | 7 | reconnect: false |
| 8 | database: gitlabhq_production | 8 | database: gitlabhq_production |
| 9 | pool: 10 | 9 | pool: 10 |
| 10 | - username: gitlab | 10 | + username: git |
| 11 | password: "secure password" | 11 | password: "secure password" |
| 12 | # host: localhost | 12 | # host: localhost |
| 13 | # socket: /tmp/mysql.sock | 13 | # socket: /tmp/mysql.sock |
config/gitlab.yml.example
| @@ -20,7 +20,7 @@ production: &base | @@ -20,7 +20,7 @@ production: &base | ||
| 20 | https: false | 20 | https: false |
| 21 | 21 | ||
| 22 | # Uncomment and customize the last line to run in a non-root path | 22 | # Uncomment and customize the last line to run in a non-root path |
| 23 | - # WARNING: This feature is known to work, but unsupported | 23 | + # WARNING: We recommend creating a FQDN to host GitLab in a root path instead of this. |
| 24 | # Note that four settings need to be changed for this to work. | 24 | # Note that four settings need to be changed for this to work. |
| 25 | # 1) In your application.rb file: config.relative_url_root = "/gitlab" | 25 | # 1) In your application.rb file: config.relative_url_root = "/gitlab" |
| 26 | # 2) In your gitlab.yml file: relative_url_root: /gitlab | 26 | # 2) In your gitlab.yml file: relative_url_root: /gitlab |
| @@ -57,11 +57,15 @@ production: &base | @@ -57,11 +57,15 @@ production: &base | ||
| 57 | # default: false - Account passwords are not sent via the email if signup is enabled. | 57 | # default: false - Account passwords are not sent via the email if signup is enabled. |
| 58 | # signup_enabled: true | 58 | # signup_enabled: true |
| 59 | 59 | ||
| 60 | + # Restrict setting visibility levels for non-admin users. | ||
| 61 | + # The default is to allow all levels. | ||
| 62 | + #restricted_visibility_levels: [ "public" ] | ||
| 63 | + | ||
| 60 | ## Automatic issue closing | 64 | ## Automatic issue closing |
| 61 | # If a commit message matches this regular expression, all issues referenced from the matched text will be closed. | 65 | # If a commit message matches this regular expression, all issues referenced from the matched text will be closed. |
| 62 | - # This happends when the commit is pushed or merged into the default branch of a project. | 66 | + # This happens when the commit is pushed or merged into the default branch of a project. |
| 63 | # When not specified the default issue_closing_pattern as specified below will be used. | 67 | # When not specified the default issue_closing_pattern as specified below will be used. |
| 64 | - # issue_closing_pattern: ([Cc]loses|[Ff]ixes) +#\d+ | 68 | + # issue_closing_pattern: ([Cc]lose[sd]|[Ff]ixe[sd]) +#\d+ |
| 65 | 69 | ||
| 66 | ## Default project features settings | 70 | ## Default project features settings |
| 67 | default_projects_features: | 71 | default_projects_features: |
| @@ -70,7 +74,7 @@ production: &base | @@ -70,7 +74,7 @@ production: &base | ||
| 70 | wiki: true | 74 | wiki: true |
| 71 | wall: false | 75 | wall: false |
| 72 | snippets: false | 76 | snippets: false |
| 73 | - public: false | 77 | + visibility_level: "private" # can be "private" | "internal" | "public" |
| 74 | 78 | ||
| 75 | ## External issues trackers | 79 | ## External issues trackers |
| 76 | issues_tracker: | 80 | issues_tracker: |
| @@ -112,6 +116,8 @@ production: &base | @@ -112,6 +116,8 @@ production: &base | ||
| 112 | # ========================== | 116 | # ========================== |
| 113 | 117 | ||
| 114 | ## LDAP settings | 118 | ## LDAP settings |
| 119 | + # You can inspect the first 100 LDAP users with login access by running: | ||
| 120 | + # bundle exec rake gitlab:ldap:check[100] RAILS_ENV=production | ||
| 115 | ldap: | 121 | ldap: |
| 116 | enabled: false | 122 | enabled: false |
| 117 | host: '_your_ldap_server' | 123 | host: '_your_ldap_server' |
| @@ -138,7 +144,7 @@ production: &base | @@ -138,7 +144,7 @@ production: &base | ||
| 138 | ## Auth providers | 144 | ## Auth providers |
| 139 | # Uncomment the following lines and fill in the data of the auth provider you want to use | 145 | # Uncomment the following lines and fill in the data of the auth provider you want to use |
| 140 | # If your favorite auth provider is not listed you can use others: | 146 | # If your favorite auth provider is not listed you can use others: |
| 141 | - # see https://github.com/gitlabhq/gitlabhq/wiki/Using-Custom-Omniauth-Providers | 147 | + # see https://github.com/gitlabhq/gitlab-public-wiki/wiki/Working-custom-omniauth-provider-configurations |
| 142 | # The 'app_id' and 'app_secret' parameters are always passed as the first two | 148 | # The 'app_id' and 'app_secret' parameters are always passed as the first two |
| 143 | # arguments, followed by optional 'args' which can be either a hash or an array. | 149 | # arguments, followed by optional 'args' which can be either a hash or an array. |
| 144 | providers: | 150 | providers: |
config/initializers/1_settings.rb
| @@ -30,6 +30,29 @@ class Settings < Settingslogic | @@ -30,6 +30,29 @@ class Settings < Settingslogic | ||
| 30 | gitlab.relative_url_root | 30 | gitlab.relative_url_root |
| 31 | ].join('') | 31 | ].join('') |
| 32 | end | 32 | end |
| 33 | + | ||
| 34 | + # check that values in `current` (string or integer) is a contant in `modul`. | ||
| 35 | + def verify_constant_array(modul, current, default) | ||
| 36 | + values = default || [] | ||
| 37 | + if !current.nil? | ||
| 38 | + values = [] | ||
| 39 | + current.each do |constant| | ||
| 40 | + values.push(verify_constant(modul, constant, nil)) | ||
| 41 | + end | ||
| 42 | + values.delete_if { |value| value.nil? } | ||
| 43 | + end | ||
| 44 | + values | ||
| 45 | + end | ||
| 46 | + | ||
| 47 | + # check that `current` (string or integer) is a contant in `modul`. | ||
| 48 | + def verify_constant(modul, current, default) | ||
| 49 | + constant = modul.constants.find{ |name| modul.const_get(name) == current } | ||
| 50 | + value = constant.nil? ? default : modul.const_get(constant) | ||
| 51 | + if current.is_a? String | ||
| 52 | + value = modul.const_get(current.upcase) rescue default | ||
| 53 | + end | ||
| 54 | + value | ||
| 55 | + end | ||
| 33 | end | 56 | end |
| 34 | end | 57 | end |
| 35 | 58 | ||
| @@ -68,6 +91,7 @@ rescue ArgumentError # no user configured | @@ -68,6 +91,7 @@ rescue ArgumentError # no user configured | ||
| 68 | '/home/' + Settings.gitlab['user'] | 91 | '/home/' + Settings.gitlab['user'] |
| 69 | end | 92 | end |
| 70 | Settings.gitlab['signup_enabled'] ||= false | 93 | Settings.gitlab['signup_enabled'] ||= false |
| 94 | +Settings.gitlab['restricted_visibility_levels'] = Settings.send(:verify_constant_array, Gitlab::VisibilityLevel, Settings.gitlab['restricted_visibility_levels'], []) | ||
| 71 | Settings.gitlab['username_changing_enabled'] = true if Settings.gitlab['username_changing_enabled'].nil? | 95 | Settings.gitlab['username_changing_enabled'] = true if Settings.gitlab['username_changing_enabled'].nil? |
| 72 | Settings.gitlab['issue_closing_pattern'] = '([Cc]loses|[Ff]ixes) #(\d+)' if Settings.gitlab['issue_closing_pattern'].nil? | 96 | Settings.gitlab['issue_closing_pattern'] = '([Cc]loses|[Ff]ixes) #(\d+)' if Settings.gitlab['issue_closing_pattern'].nil? |
| 73 | Settings.gitlab['default_projects_features'] ||= {} | 97 | Settings.gitlab['default_projects_features'] ||= {} |
| @@ -76,7 +100,7 @@ Settings.gitlab.default_projects_features['merge_requests'] = true if Settings.g | @@ -76,7 +100,7 @@ Settings.gitlab.default_projects_features['merge_requests'] = true if Settings.g | ||
| 76 | Settings.gitlab.default_projects_features['wiki'] = true if Settings.gitlab.default_projects_features['wiki'].nil? | 100 | Settings.gitlab.default_projects_features['wiki'] = true if Settings.gitlab.default_projects_features['wiki'].nil? |
| 77 | Settings.gitlab.default_projects_features['wall'] = false if Settings.gitlab.default_projects_features['wall'].nil? | 101 | Settings.gitlab.default_projects_features['wall'] = false if Settings.gitlab.default_projects_features['wall'].nil? |
| 78 | Settings.gitlab.default_projects_features['snippets'] = false if Settings.gitlab.default_projects_features['snippets'].nil? | 102 | Settings.gitlab.default_projects_features['snippets'] = false if Settings.gitlab.default_projects_features['snippets'].nil? |
| 79 | -Settings.gitlab.default_projects_features['public'] = false if Settings.gitlab.default_projects_features['public'].nil? | 103 | +Settings.gitlab.default_projects_features['visibility_level'] = Settings.send(:verify_constant, Gitlab::VisibilityLevel, Settings.gitlab.default_projects_features['visibility_level'], Gitlab::VisibilityLevel::PRIVATE) |
| 80 | 104 | ||
| 81 | # | 105 | # |
| 82 | # Gravatar | 106 | # Gravatar |
config/initializers/devise.rb
| @@ -74,8 +74,8 @@ Devise.setup do |config| | @@ -74,8 +74,8 @@ Devise.setup do |config| | ||
| 74 | # config.pepper = "2ef62d549c4ff98a5d3e0ba211e72cff592060247e3bbbb9f499af1222f876f53d39b39b823132affb32858168c79c1d7741d26499901b63c6030a42129924ef" | 74 | # config.pepper = "2ef62d549c4ff98a5d3e0ba211e72cff592060247e3bbbb9f499af1222f876f53d39b39b823132affb32858168c79c1d7741d26499901b63c6030a42129924ef" |
| 75 | 75 | ||
| 76 | # ==> Configuration for :confirmable | 76 | # ==> Configuration for :confirmable |
| 77 | - # The time you want to give your user to confirm his account. During this time | ||
| 78 | - # he will be able to access your application without confirming. Default is 0.days | 77 | + # The time you want to give a user to confirm their account. During this time |
| 78 | + # they will be able to access your application without confirming. Default is 0.days | ||
| 79 | # When confirm_within is zero, the user won't be able to sign in without confirming. | 79 | # When confirm_within is zero, the user won't be able to sign in without confirming. |
| 80 | # You can use this to let your user access some features of your application | 80 | # You can use this to let your user access some features of your application |
| 81 | # without confirming the account, but blocking it after a certain period | 81 | # without confirming the account, but blocking it after a certain period |
| @@ -101,7 +101,7 @@ Devise.setup do |config| | @@ -101,7 +101,7 @@ Devise.setup do |config| | ||
| 101 | 101 | ||
| 102 | # ==> Configuration for :validatable | 102 | # ==> Configuration for :validatable |
| 103 | # Range for password length. Default is 6..128. | 103 | # Range for password length. Default is 6..128. |
| 104 | - config.password_length = 6..128 | 104 | + config.password_length = 8..128 |
| 105 | 105 | ||
| 106 | # Email regex used to validate email formats. It simply asserts that | 106 | # Email regex used to validate email formats. It simply asserts that |
| 107 | # an one (and only one) @ exists in the given string. This is mainly | 107 | # an one (and only one) @ exists in the given string. This is mainly |
config/initializers/rack_attack.rb.example
| 1 | -# To enable rack-attack for your GitLab instance do the following: | ||
| 2 | -# 1. In config/application.rb find and uncomment the following line: | ||
| 3 | -# config.middleware.use Rack::Attack | ||
| 4 | -# 2. Rename this file to rack_attack.rb | ||
| 5 | -# 3. Review the paths_to_be_protected and add any other path you need protecting | ||
| 6 | -# 4. Restart GitLab instance | 1 | +# 1. Rename this file to rack_attack.rb |
| 2 | +# 2. Review the paths_to_be_protected and add any other path you need protecting | ||
| 7 | # | 3 | # |
| 8 | 4 | ||
| 9 | paths_to_be_protected = [ | 5 | paths_to_be_protected = [ |
| 10 | "#{Rails.application.config.relative_url_root}/users/password", | 6 | "#{Rails.application.config.relative_url_root}/users/password", |
| 11 | "#{Rails.application.config.relative_url_root}/users/sign_in", | 7 | "#{Rails.application.config.relative_url_root}/users/sign_in", |
| 8 | + "#{Rails.application.config.relative_url_root}/api/#{API::API.version}/session.json", | ||
| 9 | + "#{Rails.application.config.relative_url_root}/api/#{API::API.version}/session", | ||
| 12 | "#{Rails.application.config.relative_url_root}/users" | 10 | "#{Rails.application.config.relative_url_root}/users" |
| 13 | ] | 11 | ] |
| 14 | -Rack::Attack.throttle('protected paths', limit: 6, period: 60.seconds) do |req| | ||
| 15 | - req.ip if paths_to_be_protected.include?(req.path) && req.post? | 12 | + |
| 13 | +unless Rails.env.test? | ||
| 14 | + Rack::Attack.throttle('protected paths', limit: 10, period: 60.seconds) do |req| | ||
| 15 | + req.ip if paths_to_be_protected.include?(req.path) && req.post? | ||
| 16 | + end | ||
| 16 | end | 17 | end |
config/routes.rb
| @@ -86,9 +86,16 @@ Gitlab::Application.routes.draw do | @@ -86,9 +86,16 @@ Gitlab::Application.routes.draw do | ||
| 86 | get :test | 86 | get :test |
| 87 | end | 87 | end |
| 88 | 88 | ||
| 89 | + resources :broadcast_messages, only: [:index, :create, :destroy] | ||
| 89 | resource :logs, only: [:show] | 90 | resource :logs, only: [:show] |
| 90 | resource :background_jobs, controller: 'background_jobs', only: [:show] | 91 | resource :background_jobs, controller: 'background_jobs', only: [:show] |
| 91 | - resources :projects, constraints: { id: /[a-zA-Z.\/0-9_\-]+/ }, only: [:index, :show] | 92 | + |
| 93 | + resources :projects, constraints: { id: /[a-zA-Z.\/0-9_\-]+/ }, only: [:index, :show] do | ||
| 94 | + member do | ||
| 95 | + put :transfer | ||
| 96 | + end | ||
| 97 | + end | ||
| 98 | + | ||
| 92 | root to: "dashboard#index" | 99 | root to: "dashboard#index" |
| 93 | end | 100 | end |
| 94 | 101 | ||
| @@ -120,6 +127,7 @@ Gitlab::Application.routes.draw do | @@ -120,6 +127,7 @@ Gitlab::Application.routes.draw do | ||
| 120 | delete :leave | 127 | delete :leave |
| 121 | end | 128 | end |
| 122 | end | 129 | end |
| 130 | + resource :avatar, only: [:destroy] | ||
| 123 | end | 131 | end |
| 124 | end | 132 | end |
| 125 | 133 | ||
| @@ -166,7 +174,7 @@ Gitlab::Application.routes.draw do | @@ -166,7 +174,7 @@ Gitlab::Application.routes.draw do | ||
| 166 | end | 174 | end |
| 167 | 175 | ||
| 168 | scope module: :projects do | 176 | scope module: :projects do |
| 169 | - resources :blob, only: [:show], constraints: {id: /.+/} | 177 | + resources :blob, only: [:show, :destroy], constraints: {id: /.+/} |
| 170 | resources :raw, only: [:show], constraints: {id: /.+/} | 178 | resources :raw, only: [:show], constraints: {id: /.+/} |
| 171 | resources :tree, only: [:show], constraints: {id: /.+/, format: /(html|js)/ } | 179 | resources :tree, only: [:show], constraints: {id: /.+/, format: /(html|js)/ } |
| 172 | resources :edit_tree, only: [:show, :update], constraints: {id: /.+/}, path: 'edit' | 180 | resources :edit_tree, only: [:show, :update], constraints: {id: /.+/}, path: 'edit' |
config/unicorn.rb.example
| @@ -9,7 +9,7 @@ | @@ -9,7 +9,7 @@ | ||
| 9 | # documentation. | 9 | # documentation. |
| 10 | 10 | ||
| 11 | # Uncomment and customize the last line to run in a non-root path | 11 | # Uncomment and customize the last line to run in a non-root path |
| 12 | -# WARNING: This feature is known to work, but unsupported | 12 | +# WARNING: We recommend creating a FQDN to host GitLab in a root path instead of this. |
| 13 | # Note that four settings need to be changed for this to work. | 13 | # Note that four settings need to be changed for this to work. |
| 14 | # 1) In your application.rb file: config.relative_url_root = "/gitlab" | 14 | # 1) In your application.rb file: config.relative_url_root = "/gitlab" |
| 15 | # 2) In your gitlab.yml file: relative_url_root: /gitlab | 15 | # 2) In your gitlab.yml file: relative_url_root: /gitlab |
| @@ -0,0 +1,12 @@ | @@ -0,0 +1,12 @@ | ||
| 1 | +class CreateBroadcastMessages < ActiveRecord::Migration | ||
| 2 | + def change | ||
| 3 | + create_table :broadcast_messages do |t| | ||
| 4 | + t.text :message, null: false | ||
| 5 | + t.datetime :starts_at | ||
| 6 | + t.datetime :ends_at | ||
| 7 | + t.integer :alert_type | ||
| 8 | + | ||
| 9 | + t.timestamps | ||
| 10 | + end | ||
| 11 | + end | ||
| 12 | +end |
db/migrate/20131112220935_add_visibility_level_to_projects.rb
0 → 100644
| @@ -0,0 +1,13 @@ | @@ -0,0 +1,13 @@ | ||
| 1 | +class AddVisibilityLevelToProjects < ActiveRecord::Migration | ||
| 2 | + def self.up | ||
| 3 | + add_column :projects, :visibility_level, :integer, :default => 0, :null => false | ||
| 4 | + Project.where(public: true).update_all(visibility_level: Gitlab::VisibilityLevel::PUBLIC) | ||
| 5 | + remove_column :projects, :public | ||
| 6 | + end | ||
| 7 | + | ||
| 8 | + def self.down | ||
| 9 | + add_column :projects, :public, :boolean, :default => false, :null => false | ||
| 10 | + Project.where(visibility_level: Gitlab::VisibilityLevel::PUBLIC).update_all(public: true) | ||
| 11 | + remove_column :projects, :visibility_level | ||
| 12 | + end | ||
| 13 | +end |
db/migrate/20131202192556_add_event_fields_for_web_hook.rb
0 → 100644
| @@ -0,0 +1,7 @@ | @@ -0,0 +1,7 @@ | ||
| 1 | +class AddEventFieldsForWebHook < ActiveRecord::Migration | ||
| 2 | + def change | ||
| 3 | + add_column :web_hooks, :push_events, :boolean, default: true, null: false | ||
| 4 | + add_column :web_hooks, :issues_events, :boolean, default: false, null: false | ||
| 5 | + add_column :web_hooks, :merge_requests_events, :boolean, default: false, null: false | ||
| 6 | + end | ||
| 7 | +end |
db/schema.rb
| @@ -11,7 +11,16 @@ | @@ -11,7 +11,16 @@ | ||
| 11 | # | 11 | # |
| 12 | # It's strongly recommended to check this file into your version control system. | 12 | # It's strongly recommended to check this file into your version control system. |
| 13 | 13 | ||
| 14 | -ActiveRecord::Schema.define(:version => 20131009115346) do | 14 | +ActiveRecord::Schema.define(:version => 20131202192556) do |
| 15 | + | ||
| 16 | + create_table "broadcast_messages", :force => true do |t| | ||
| 17 | + t.text "message", :null => false | ||
| 18 | + t.datetime "starts_at" | ||
| 19 | + t.datetime "ends_at" | ||
| 20 | + t.integer "alert_type" | ||
| 21 | + t.datetime "created_at", :null => false | ||
| 22 | + t.datetime "updated_at", :null => false | ||
| 23 | + end | ||
| 15 | 24 | ||
| 16 | create_table "deploy_keys_projects", :force => true do |t| | 25 | create_table "deploy_keys_projects", :force => true do |t| |
| 17 | t.integer "deploy_key_id", :null => false | 26 | t.integer "deploy_key_id", :null => false |
| @@ -171,19 +180,18 @@ ActiveRecord::Schema.define(:version => 20131009115346) do | @@ -171,19 +180,18 @@ ActiveRecord::Schema.define(:version => 20131009115346) do | ||
| 171 | t.datetime "created_at", :null => false | 180 | t.datetime "created_at", :null => false |
| 172 | t.datetime "updated_at", :null => false | 181 | t.datetime "updated_at", :null => false |
| 173 | t.integer "creator_id" | 182 | t.integer "creator_id" |
| 174 | - t.string "default_branch" | ||
| 175 | t.boolean "issues_enabled", :default => true, :null => false | 183 | t.boolean "issues_enabled", :default => true, :null => false |
| 176 | t.boolean "wall_enabled", :default => true, :null => false | 184 | t.boolean "wall_enabled", :default => true, :null => false |
| 177 | t.boolean "merge_requests_enabled", :default => true, :null => false | 185 | t.boolean "merge_requests_enabled", :default => true, :null => false |
| 178 | t.boolean "wiki_enabled", :default => true, :null => false | 186 | t.boolean "wiki_enabled", :default => true, :null => false |
| 179 | t.integer "namespace_id" | 187 | t.integer "namespace_id" |
| 180 | - t.boolean "public", :default => false, :null => false | ||
| 181 | t.string "issues_tracker", :default => "gitlab", :null => false | 188 | t.string "issues_tracker", :default => "gitlab", :null => false |
| 182 | t.string "issues_tracker_id" | 189 | t.string "issues_tracker_id" |
| 183 | t.boolean "snippets_enabled", :default => true, :null => false | 190 | t.boolean "snippets_enabled", :default => true, :null => false |
| 184 | t.datetime "last_activity_at" | 191 | t.datetime "last_activity_at" |
| 185 | t.boolean "imported", :default => false, :null => false | 192 | t.boolean "imported", :default => false, :null => false |
| 186 | t.string "import_url" | 193 | t.string "import_url" |
| 194 | + t.integer "visibility_level", :default => 0, :null => false | ||
| 187 | end | 195 | end |
| 188 | 196 | ||
| 189 | add_index "projects", ["creator_id"], :name => "index_projects_on_owner_id" | 197 | add_index "projects", ["creator_id"], :name => "index_projects_on_owner_id" |
| @@ -326,10 +334,13 @@ ActiveRecord::Schema.define(:version => 20131009115346) do | @@ -326,10 +334,13 @@ ActiveRecord::Schema.define(:version => 20131009115346) do | ||
| 326 | create_table "web_hooks", :force => true do |t| | 334 | create_table "web_hooks", :force => true do |t| |
| 327 | t.string "url" | 335 | t.string "url" |
| 328 | t.integer "project_id" | 336 | t.integer "project_id" |
| 329 | - t.datetime "created_at", :null => false | ||
| 330 | - t.datetime "updated_at", :null => false | ||
| 331 | - t.string "type", :default => "ProjectHook" | 337 | + t.datetime "created_at", :null => false |
| 338 | + t.datetime "updated_at", :null => false | ||
| 339 | + t.string "type", :default => "ProjectHook" | ||
| 332 | t.integer "service_id" | 340 | t.integer "service_id" |
| 341 | + t.boolean "push_events", :default => true, :null => false | ||
| 342 | + t.boolean "issues_events", :default => false, :null => false | ||
| 343 | + t.boolean "merge_requests_events", :default => false, :null => false | ||
| 333 | end | 344 | end |
| 334 | 345 | ||
| 335 | add_index "web_hooks", ["project_id"], :name => "index_web_hooks_on_project_id" | 346 | add_index "web_hooks", ["project_id"], :name => "index_web_hooks_on_project_id" |
doc/api/projects.md
| @@ -2,7 +2,7 @@ | @@ -2,7 +2,7 @@ | ||
| 2 | 2 | ||
| 3 | ### List projects | 3 | ### List projects |
| 4 | 4 | ||
| 5 | -Get a list of projects owned by the authenticated user. | 5 | +Get a list of projects accessible by the authenticated user. |
| 6 | 6 | ||
| 7 | ``` | 7 | ``` |
| 8 | GET /projects | 8 | GET /projects |
| @@ -15,6 +15,7 @@ GET /projects | @@ -15,6 +15,7 @@ GET /projects | ||
| 15 | "description": null, | 15 | "description": null, |
| 16 | "default_branch": "master", | 16 | "default_branch": "master", |
| 17 | "public": false, | 17 | "public": false, |
| 18 | + "visibility_level": 0, | ||
| 18 | "ssh_url_to_repo": "git@example.com:diaspora/diaspora-client.git", | 19 | "ssh_url_to_repo": "git@example.com:diaspora/diaspora-client.git", |
| 19 | "http_url_to_repo": "http://example.com/diaspora/diaspora-client.git", | 20 | "http_url_to_repo": "http://example.com/diaspora/diaspora-client.git", |
| 20 | "web_url": "http://example.com/diaspora/diaspora-client", | 21 | "web_url": "http://example.com/diaspora/diaspora-client", |
| @@ -49,6 +50,7 @@ GET /projects | @@ -49,6 +50,7 @@ GET /projects | ||
| 49 | "description": null, | 50 | "description": null, |
| 50 | "default_branch": "master", | 51 | "default_branch": "master", |
| 51 | "public": false, | 52 | "public": false, |
| 53 | + "visibility_level": 0, | ||
| 52 | "ssh_url_to_repo": "git@example.com:brightbox/puppet.git", | 54 | "ssh_url_to_repo": "git@example.com:brightbox/puppet.git", |
| 53 | "http_url_to_repo": "http://example.com/brightbox/puppet.git", | 55 | "http_url_to_repo": "http://example.com/brightbox/puppet.git", |
| 54 | "web_url": "http://example.com/brightbox/puppet", | 56 | "web_url": "http://example.com/brightbox/puppet", |
| @@ -82,6 +84,22 @@ GET /projects | @@ -82,6 +84,22 @@ GET /projects | ||
| 82 | ``` | 84 | ``` |
| 83 | 85 | ||
| 84 | 86 | ||
| 87 | +#### List owned projects | ||
| 88 | + | ||
| 89 | +Get a list of projects owned by the authenticated user. | ||
| 90 | + | ||
| 91 | +``` | ||
| 92 | +GET /projects/owned | ||
| 93 | +``` | ||
| 94 | + | ||
| 95 | +#### List ALL projects | ||
| 96 | + | ||
| 97 | +Get a list of all GitLab projects (admin only). | ||
| 98 | + | ||
| 99 | +``` | ||
| 100 | +GET /projects/all | ||
| 101 | +``` | ||
| 102 | + | ||
| 85 | ### Get single project | 103 | ### Get single project |
| 86 | 104 | ||
| 87 | Get a specific project, identified by project ID or NAMESPACE/PROJECT_NAME , which is owned by the authentication user. | 105 | Get a specific project, identified by project ID or NAMESPACE/PROJECT_NAME , which is owned by the authentication user. |
| @@ -101,6 +119,7 @@ Parameters: | @@ -101,6 +119,7 @@ Parameters: | ||
| 101 | "description": null, | 119 | "description": null, |
| 102 | "default_branch": "master", | 120 | "default_branch": "master", |
| 103 | "public": false, | 121 | "public": false, |
| 122 | + "visibility_level": 0, | ||
| 104 | "ssh_url_to_repo": "git@example.com:diaspora/diaspora-project-site.git", | 123 | "ssh_url_to_repo": "git@example.com:diaspora/diaspora-project-site.git", |
| 105 | "http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git", | 124 | "http_url_to_repo": "http://example.com/diaspora/diaspora-project-site.git", |
| 106 | "web_url": "http://example.com/diaspora/diaspora-project-site", | 125 | "web_url": "http://example.com/diaspora/diaspora-project-site", |
| @@ -213,13 +232,13 @@ Parameters: | @@ -213,13 +232,13 @@ Parameters: | ||
| 213 | 232 | ||
| 214 | + `name` (required) - new project name | 233 | + `name` (required) - new project name |
| 215 | + `description` (optional) - short project description | 234 | + `description` (optional) - short project description |
| 216 | -+ `default_branch` (optional) - 'master' by default | ||
| 217 | + `issues_enabled` (optional) | 235 | + `issues_enabled` (optional) |
| 218 | + `wall_enabled` (optional) | 236 | + `wall_enabled` (optional) |
| 219 | + `merge_requests_enabled` (optional) | 237 | + `merge_requests_enabled` (optional) |
| 220 | + `wiki_enabled` (optional) | 238 | + `wiki_enabled` (optional) |
| 221 | + `snippets_enabled` (optional) | 239 | + `snippets_enabled` (optional) |
| 222 | -+ `public` (optional) | 240 | ++ `public` (optional) - if `true` same as setting visibility_level = 20 |
| 241 | ++ `visibility_level` (optional) | ||
| 223 | 242 | ||
| 224 | 243 | ||
| 225 | ### Create project for user | 244 | ### Create project for user |
| @@ -241,7 +260,8 @@ Parameters: | @@ -241,7 +260,8 @@ Parameters: | ||
| 241 | + `merge_requests_enabled` (optional) | 260 | + `merge_requests_enabled` (optional) |
| 242 | + `wiki_enabled` (optional) | 261 | + `wiki_enabled` (optional) |
| 243 | + `snippets_enabled` (optional) | 262 | + `snippets_enabled` (optional) |
| 244 | -+ `public` (optional) | 263 | ++ `public` (optional) - if `true` same as setting visibility_level = 20 |
| 264 | ++ `visibility_level` (optional) | ||
| 245 | 265 | ||
| 246 | 266 | ||
| 247 | ## Remove project | 267 | ## Remove project |
| @@ -382,6 +402,10 @@ Parameters: | @@ -382,6 +402,10 @@ Parameters: | ||
| 382 | { | 402 | { |
| 383 | "id": 1, | 403 | "id": 1, |
| 384 | "url": "http://example.com/hook", | 404 | "url": "http://example.com/hook", |
| 405 | + "project_id": 3, | ||
| 406 | + "push_events": "true", | ||
| 407 | + "issues_events": "true", | ||
| 408 | + "merge_requests_events": "true", | ||
| 385 | "created_at": "2012-10-12T17:04:47Z" | 409 | "created_at": "2012-10-12T17:04:47Z" |
| 386 | } | 410 | } |
| 387 | ``` | 411 | ``` |
| @@ -399,6 +423,9 @@ Parameters: | @@ -399,6 +423,9 @@ Parameters: | ||
| 399 | 423 | ||
| 400 | + `id` (required) - The ID or NAME of a project | 424 | + `id` (required) - The ID or NAME of a project |
| 401 | + `url` (required) - The hook URL | 425 | + `url` (required) - The hook URL |
| 426 | ++ `push_events` - Trigger hook on push events | ||
| 427 | ++ `issues_events` - Trigger hook on issues events | ||
| 428 | ++ `merge_requests_events` - Trigger hook on merge_requests events | ||
| 402 | 429 | ||
| 403 | 430 | ||
| 404 | ### Edit project hook | 431 | ### Edit project hook |
| @@ -414,6 +441,9 @@ Parameters: | @@ -414,6 +441,9 @@ Parameters: | ||
| 414 | + `id` (required) - The ID or NAME of a project | 441 | + `id` (required) - The ID or NAME of a project |
| 415 | + `hook_id` (required) - The ID of a project hook | 442 | + `hook_id` (required) - The ID of a project hook |
| 416 | + `url` (required) - The hook URL | 443 | + `url` (required) - The hook URL |
| 444 | ++ `push_events` - Trigger hook on push events | ||
| 445 | ++ `issues_events` - Trigger hook on issues events | ||
| 446 | ++ `merge_requests_events` - Trigger hook on merge_requests events | ||
| 417 | 447 | ||
| 418 | 448 | ||
| 419 | ### Delete project hook | 449 | ### Delete project hook |
| @@ -458,7 +488,7 @@ Parameters: | @@ -458,7 +488,7 @@ Parameters: | ||
| 458 | "id":"3f94fc7c85061973edc9906ae170cc269b07ca55" | 488 | "id":"3f94fc7c85061973edc9906ae170cc269b07ca55" |
| 459 | }], | 489 | }], |
| 460 | "tree": "c68537c6534a02cc2b176ca1549f4ffa190b58ee", | 490 | "tree": "c68537c6534a02cc2b176ca1549f4ffa190b58ee", |
| 461 | - "message":"give caolan his credit where it's due (up top)", | 491 | + "message":"give caolan credit where it's due (up top)", |
| 462 | "author": { | 492 | "author": { |
| 463 | "name":"Jeremy Ashkenas", | 493 | "name":"Jeremy Ashkenas", |
| 464 | "email":"jashkenas@example.com" | 494 | "email":"jashkenas@example.com" |
doc/api/repositories.md
| @@ -368,4 +368,43 @@ GET /projects/:id/repository/archive | @@ -368,4 +368,43 @@ GET /projects/:id/repository/archive | ||
| 368 | 368 | ||
| 369 | Parameters: | 369 | Parameters: |
| 370 | + `id` (required) - The ID of a project | 370 | + `id` (required) - The ID of a project |
| 371 | -+ `sha` (optional) - The commit sha to download defaults to the tip of the default branch | ||
| 372 | \ No newline at end of file | 371 | \ No newline at end of file |
| 372 | ++ `sha` (optional) - The commit sha to download defaults to the tip of the default branch | ||
| 373 | + | ||
| 374 | + | ||
| 375 | +## Create new file in repository | ||
| 376 | + | ||
| 377 | +``` | ||
| 378 | +POST /projects/:id/repository/files | ||
| 379 | +``` | ||
| 380 | + | ||
| 381 | +Parameters: | ||
| 382 | + | ||
| 383 | ++ `file_path` (optional) - Full path to new file. Ex. lib/class.rb | ||
| 384 | ++ `branch_name` (required) - The name of branch | ||
| 385 | ++ `content` (required) - File content | ||
| 386 | ++ `commit_message` (required) - Commit message | ||
| 387 | + | ||
| 388 | +## Update existing file in repository | ||
| 389 | + | ||
| 390 | +``` | ||
| 391 | +PUT /projects/:id/repository/files | ||
| 392 | +``` | ||
| 393 | + | ||
| 394 | +Parameters: | ||
| 395 | + | ||
| 396 | ++ `file_path` (required) - Full path to file. Ex. lib/class.rb | ||
| 397 | ++ `branch_name` (required) - The name of branch | ||
| 398 | ++ `content` (required) - New file content | ||
| 399 | ++ `commit_message` (required) - Commit message | ||
| 400 | + | ||
| 401 | +## Delete existing file in repository | ||
| 402 | + | ||
| 403 | +``` | ||
| 404 | +DELETE /projects/:id/repository/files | ||
| 405 | +``` | ||
| 406 | + | ||
| 407 | +Parameters: | ||
| 408 | + | ||
| 409 | ++ `file_path` (required) - Full path to file. Ex. lib/class.rb | ||
| 410 | ++ `branch_name` (required) - The name of branch | ||
| 411 | ++ `commit_message` (required) - Commit message |
| @@ -0,0 +1,23 @@ | @@ -0,0 +1,23 @@ | ||
| 1 | +# GitLab project architecture | ||
| 2 | + | ||
| 3 | +GitLab project consists of two parts: GitLab and GitLab shell. | ||
| 4 | + | ||
| 5 | +## GitLab | ||
| 6 | + | ||
| 7 | +Web application with background jobs workers. | ||
| 8 | +Provides you with UI and most of functionality. | ||
| 9 | +For some operations like repo creation - uses GitLab shell. | ||
| 10 | + | ||
| 11 | +Uses: | ||
| 12 | + * Ruby as main language for application code and most libraries. | ||
| 13 | + * [Rails](http://rubyonrails.org/) web framework as main framework for application. | ||
| 14 | + * Mysql or postgres as main databases. Used for persistent data storage(users, project, issues etc). | ||
| 15 | + * Redis database. Used for cache and exchange data between some components. | ||
| 16 | + * Python2 because of [pygments](http://pygments.org/) as code syntax highlighter. | ||
| 17 | + | ||
| 18 | +## GitLab shell | ||
| 19 | + | ||
| 20 | +Command line ruby application. Used by GitLab through shell commands. | ||
| 21 | +It provides interface to all kind of manipulations with repositories and ssh keys. | ||
| 22 | +Full list of commands you can find in README of GitLab shell repo. | ||
| 23 | +Works on pure ruby and do not require any additional software. |
doc/install/databases.md
| @@ -25,19 +25,19 @@ GitLab supports the following databases: | @@ -25,19 +25,19 @@ GitLab supports the following databases: | ||
| 25 | # Create a user for GitLab | 25 | # Create a user for GitLab |
| 26 | # do not type the 'mysql>', this is part of the prompt | 26 | # do not type the 'mysql>', this is part of the prompt |
| 27 | # change $password in the command below to a real password you pick | 27 | # change $password in the command below to a real password you pick |
| 28 | - mysql> CREATE USER 'gitlab'@'localhost' IDENTIFIED BY '$password'; | 28 | + mysql> CREATE USER 'git'@'localhost' IDENTIFIED BY '$password'; |
| 29 | 29 | ||
| 30 | # Create the GitLab production database | 30 | # Create the GitLab production database |
| 31 | mysql> CREATE DATABASE IF NOT EXISTS `gitlabhq_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`; | 31 | mysql> CREATE DATABASE IF NOT EXISTS `gitlabhq_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`; |
| 32 | 32 | ||
| 33 | # Grant the GitLab user necessary permissions on the table. | 33 | # Grant the GitLab user necessary permissions on the table. |
| 34 | - mysql> GRANT SELECT, LOCK TABLES, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER ON `gitlabhq_production`.* TO 'gitlab'@'localhost'; | 34 | + mysql> GRANT SELECT, LOCK TABLES, INSERT, UPDATE, DELETE, CREATE, DROP, INDEX, ALTER ON `gitlabhq_production`.* TO 'git'@'localhost'; |
| 35 | 35 | ||
| 36 | # Quit the database session | 36 | # Quit the database session |
| 37 | mysql> \q | 37 | mysql> \q |
| 38 | 38 | ||
| 39 | # Try connecting to the new database with the new user | 39 | # Try connecting to the new database with the new user |
| 40 | - sudo -u git -H mysql -u gitlab -p -D gitlabhq_production | 40 | + sudo -u git -H mysql -u git -p -D gitlabhq_production |
| 41 | 41 | ||
| 42 | # Type the password you replaced $password with earlier | 42 | # Type the password you replaced $password with earlier |
| 43 | 43 |
doc/install/installation.md
| @@ -118,8 +118,8 @@ Remove the old Ruby 1.8 if present | @@ -118,8 +118,8 @@ Remove the old Ruby 1.8 if present | ||
| 118 | Download Ruby and compile it: | 118 | Download Ruby and compile it: |
| 119 | 119 | ||
| 120 | mkdir /tmp/ruby && cd /tmp/ruby | 120 | mkdir /tmp/ruby && cd /tmp/ruby |
| 121 | - curl --progress ftp://ftp.ruby-lang.org/pub/ruby/2.0/ruby-2.0.0-p247.tar.gz | tar xz | ||
| 122 | - cd ruby-2.0.0-p247 | 121 | + curl --progress ftp://ftp.ruby-lang.org/pub/ruby/2.0/ruby-2.0.0-p353.tar.gz | tar xz |
| 122 | + cd ruby-2.0.0-p353 | ||
| 123 | ./configure --disable-install-rdoc | 123 | ./configure --disable-install-rdoc |
| 124 | make | 124 | make |
| 125 | sudo make install | 125 | sudo make install |
| @@ -149,7 +149,7 @@ GitLab Shell is an ssh access and repository management software developed speci | @@ -149,7 +149,7 @@ GitLab Shell is an ssh access and repository management software developed speci | ||
| 149 | cd gitlab-shell | 149 | cd gitlab-shell |
| 150 | 150 | ||
| 151 | # switch to right version | 151 | # switch to right version |
| 152 | - sudo -u git -H git checkout v1.7.4 | 152 | + sudo -u git -H git checkout v1.7.9 |
| 153 | 153 | ||
| 154 | sudo -u git -H cp config.yml.example config.yml | 154 | sudo -u git -H cp config.yml.example config.yml |
| 155 | 155 | ||
| @@ -180,10 +180,10 @@ To setup the MySQL/PostgreSQL database and dependencies please see [`doc/install | @@ -180,10 +180,10 @@ To setup the MySQL/PostgreSQL database and dependencies please see [`doc/install | ||
| 180 | cd /home/git/gitlab | 180 | cd /home/git/gitlab |
| 181 | 181 | ||
| 182 | # Checkout to stable release | 182 | # Checkout to stable release |
| 183 | - sudo -u git -H git checkout 6-2-stable | 183 | + sudo -u git -H git checkout 6-3-stable |
| 184 | 184 | ||
| 185 | **Note:** | 185 | **Note:** |
| 186 | -You can change `6-2-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server! | 186 | +You can change `6-3-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server! |
| 187 | 187 | ||
| 188 | ## Configure it | 188 | ## Configure it |
| 189 | 189 | ||
| @@ -227,10 +227,6 @@ You can change `6-2-stable` to `master` if you want the *bleeding edge* version, | @@ -227,10 +227,6 @@ You can change `6-2-stable` to `master` if you want the *bleeding edge* version, | ||
| 227 | # Copy the example Rack attack config | 227 | # Copy the example Rack attack config |
| 228 | sudo -u git -H cp config/initializers/rack_attack.rb.example config/initializers/rack_attack.rb | 228 | sudo -u git -H cp config/initializers/rack_attack.rb.example config/initializers/rack_attack.rb |
| 229 | 229 | ||
| 230 | - # Enable rack attack middleware | ||
| 231 | - # Find and uncomment the line 'config.middleware.use Rack::Attack' | ||
| 232 | - sudo -u git -H editor config/application.rb | ||
| 233 | - | ||
| 234 | # Configure Git global settings for git user, useful when editing via web | 230 | # Configure Git global settings for git user, useful when editing via web |
| 235 | # Edit user.email according to what is set in gitlab.yml | 231 | # Edit user.email according to what is set in gitlab.yml |
| 236 | sudo -u git -H git config --global user.name "GitLab" | 232 | sudo -u git -H git config --global user.name "GitLab" |
| @@ -265,8 +261,6 @@ Make sure to edit both `gitlab.yml` and `unicorn.rb` to match your setup. | @@ -265,8 +261,6 @@ Make sure to edit both `gitlab.yml` and `unicorn.rb` to match your setup. | ||
| 265 | 261 | ||
| 266 | cd /home/git/gitlab | 262 | cd /home/git/gitlab |
| 267 | 263 | ||
| 268 | - sudo gem install charlock_holmes --version '0.6.9.4' | ||
| 269 | - | ||
| 270 | # For MySQL (note, the option says "without ... postgres") | 264 | # For MySQL (note, the option says "without ... postgres") |
| 271 | sudo -u git -H bundle install --deployment --without development test postgres aws | 265 | sudo -u git -H bundle install --deployment --without development test postgres aws |
| 272 | 266 | ||
| @@ -428,5 +422,5 @@ These steps are fairly general and you will need to figure out the exact details | @@ -428,5 +422,5 @@ These steps are fairly general and you will need to figure out the exact details | ||
| 428 | ### Examples | 422 | ### Examples |
| 429 | 423 | ||
| 430 | If you have successfully set up a provider that is not shipped with GitLab itself, please let us know. | 424 | If you have successfully set up a provider that is not shipped with GitLab itself, please let us know. |
| 431 | -You can help others by reporting successful configurations and probably share a few insights or provide warnings for common errors or pitfalls by sharing your experience [in the public Wiki](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Working-Custom-Omniauth-Provider-Configurations). | 425 | +You can help others by reporting successful configurations and probably share a few insights or provide warnings for common errors or pitfalls by sharing your experience [in the public Wiki](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Custom-omniauth-provider-configurations). |
| 432 | While we can't officially support every possible auth mechanism out there, we'd like to at least help those with special needs. | 426 | While we can't officially support every possible auth mechanism out there, we'd like to at least help those with special needs. |
doc/install/requirements.md
| 1 | # Operating Systems | 1 | # Operating Systems |
| 2 | 2 | ||
| 3 | -## Linux | ||
| 4 | - | ||
| 5 | GitLab is developed for the Linux operating system. | 3 | GitLab is developed for the Linux operating system. |
| 6 | 4 | ||
| 7 | -GitLab officially supports (recent versions of) these Linux distributions: | 5 | +## GitLab officially supports |
| 8 | 6 | ||
| 9 | - Ubuntu Linux | 7 | - Ubuntu Linux |
| 10 | - Debian/GNU Linux | 8 | - Debian/GNU Linux |
| 11 | 9 | ||
| 12 | -It should also work on (though they are not officially supported): | 10 | +## GitLab.com offers paid support for |
| 13 | 11 | ||
| 14 | -- Arch | 12 | +- Red Hat Enterprise Linux (RHEL) |
| 15 | - CentOS | 13 | - CentOS |
| 14 | +- Oracle Linux | ||
| 15 | + | ||
| 16 | +## Not officially supported | ||
| 17 | + | ||
| 18 | +- Arch Linux | ||
| 16 | - Fedora | 19 | - Fedora |
| 17 | - Gentoo | 20 | - Gentoo |
| 18 | -- RHEL | ||
| 19 | 21 | ||
| 20 | -## Other Unix Systems | 22 | +On the above distributions it is pretty easy to install GitLab yourself. |
| 23 | + | ||
| 24 | +## Unsupported Unix Systems | ||
| 21 | 25 | ||
| 22 | -There is nothing that prevents GitLab from running on other Unix operating | ||
| 23 | -systems. This means you may get it to work on systems running FreeBSD or OS X. | ||
| 24 | -**If you want to try, please proceed with caution!** | 26 | +There is nothing that prevents GitLab from running on other Unix operating systems. |
| 27 | +This means you may get it to work on systems running FreeBSD or OS X. | ||
| 28 | +If you want to do this, please be aware it could be a lot of work. | ||
| 29 | +Please consider using a virtual machine to run GitLab. | ||
| 25 | 30 | ||
| 26 | -## Windows | 31 | +## Other operating systems such as Windows |
| 27 | 32 | ||
| 28 | -GitLab does **not** run on Windows and we have no plans of supporting it in the | ||
| 29 | -near future. Please consider using a virtual machine to run GitLab. | 33 | +GitLab does **not** run on Windows and we have no plans of supporting it in the near future. |
| 34 | +Please consider using a virtual machine to run GitLab. | ||
| 30 | 35 | ||
| 31 | 36 | ||
| 32 | -# Rubies | 37 | +# Ruby versions |
| 33 | 38 | ||
| 34 | -GitLab requires Ruby (MRI) 1.9.3 and several Gems with native components. | ||
| 35 | -While it is generally possible to use other Rubies (like | ||
| 36 | -[JRuby](http://jruby.org/) or [Rubinius](http://rubini.us/)) it might require | ||
| 37 | -some work on your part. | 39 | +GitLab requires Ruby (MRI) 1.9.3 or 2.0+. |
| 40 | +While it is generally possible to use other Rubies | ||
| 41 | +(like [JRuby](http://jruby.org/) or [Rubinius](http://rubini.us/)) | ||
| 42 | +it might require some work since GitLab uses several Gems that have native extensions. | ||
| 38 | 43 | ||
| 39 | 44 | ||
| 40 | # Hardware requirements | 45 | # Hardware requirements |
| @@ -0,0 +1,25 @@ | @@ -0,0 +1,25 @@ | ||
| 1 | +You accept and agree to the following terms and conditions for Your present and future Contributions submitted to GitLab.com. Except for the license granted herein to GitLab.com and recipients of software distributed by GitLab.com, You reserve all right, title, and interest in and to Your Contributions. | ||
| 2 | + | ||
| 3 | +1. Definitions. | ||
| 4 | + | ||
| 5 | + "You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with GitLab.com. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. | ||
| 6 | + | ||
| 7 | + "Contribution" shall mean the code, documentation or other original works of authorship expressly identified in Schedule B, as well as any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to GitLab.com for inclusion in, or documentation of, any of the products owned or managed by GitLab.com (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to GitLab.com or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, GitLab.com for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution." | ||
| 8 | + | ||
| 9 | +2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to GitLab.com and to recipients of software distributed by GitLab.com a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works. | ||
| 10 | + | ||
| 11 | +3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to GitLab.com and to recipients of software distributed by GitLab.com a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed. | ||
| 12 | + | ||
| 13 | +4. You represent that You are legally entitled to grant the above license. You represent further that each employee of the Corporation designated on Schedule A below (or in a subsequent written modification to that Schedule) is authorized to submit Contributions on behalf of the Corporation. | ||
| 14 | + | ||
| 15 | +5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others). | ||
| 16 | + | ||
| 17 | +6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. | ||
| 18 | + | ||
| 19 | +7. Should You wish to submit work that is not Your original creation, You may submit it to GitLab.com separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]". | ||
| 20 | + | ||
| 21 | +8. It is your responsibility to notify GitLab.com when any change is required to the list of designated employees authorized to submit Contributions on behalf of the Corporation, or to the Corporation's Point of Contact with GitLab.com. | ||
| 22 | + | ||
| 23 | +--------------------------------------- | ||
| 24 | + | ||
| 25 | +This text is licensed under the [Creative Commons Attribution 3.0 License](http://creativecommons.org/licenses/by/3.0/) and the original source is the Google Open Source Programs Office. |
| @@ -0,0 +1,25 @@ | @@ -0,0 +1,25 @@ | ||
| 1 | +You accept and agree to the following terms and conditions for Your present and future Contributions submitted to GitLab.com. Except for the license granted herein to GitLab.com and recipients of software distributed by GitLab.com, You reserve all right, title, and interest in and to Your Contributions. | ||
| 2 | + | ||
| 3 | +1. Definitions. | ||
| 4 | + | ||
| 5 | + "You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner that is making this Agreement with GitLab.com. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. | ||
| 6 | + | ||
| 7 | + "Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is intentionally submitted by You to GitLab.com for inclusion in, or documentation of, any of the products owned or managed by GitLab.com (the "Work"). For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to GitLab.com or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, GitLab.com for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by You as "Not a Contribution." | ||
| 8 | + | ||
| 9 | +2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to GitLab.com and to recipients of software distributed by GitLab.com a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and such derivative works. | ||
| 10 | + | ||
| 11 | +3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to GitLab.com and to recipients of software distributed by GitLab.com a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed. | ||
| 12 | + | ||
| 13 | +4. You represent that you are legally entitled to grant the above license. If your employer(s) has rights to intellectual property that you create that includes your Contributions, you represent that you have received permission to make Contributions on behalf of that employer, that your employer has waived such rights for your Contributions to GitLab.com, or that your employer has executed a separate Corporate CLA with GitLab.com. | ||
| 14 | + | ||
| 15 | +5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of others). You represent that Your Contribution submissions include complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which you are personally aware and which are associated with any part of Your Contributions. | ||
| 16 | + | ||
| 17 | +6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support. You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON- INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. | ||
| 18 | + | ||
| 19 | +7. Should You wish to submit work that is not Your original creation, You may submit it to GitLab.com separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [[]named here]". | ||
| 20 | + | ||
| 21 | +8. You agree to notify GitLab.com of any facts or circumstances of which you become aware that would make these representations inaccurate in any respect. | ||
| 22 | + | ||
| 23 | +--------------------------------------- | ||
| 24 | + | ||
| 25 | +This text is licensed under the [Creative Commons Attribution 3.0 License](http://creativecommons.org/licenses/by/3.0/) and the original source is the Google Open Source Programs Office. |
doc/make_release.md
| @@ -1,76 +0,0 @@ | @@ -1,76 +0,0 @@ | ||
| 1 | -# Things to do when creating new release | ||
| 2 | -NOTE: This is a guide for GitLab developers. If you are trying to install GitLab see the latest stable [installation guide](install/installation.md) and if you are trying to upgrade, see the [upgrade guides](update). | ||
| 3 | - | ||
| 4 | -## Install guide up to date? | ||
| 5 | - | ||
| 6 | -* References correct GitLab branch `x-x-stable` and correct GitLab shell tag? | ||
| 7 | - | ||
| 8 | -## Make upgrade guide | ||
| 9 | - | ||
| 10 | -### From x.x to x.x | ||
| 11 | - | ||
| 12 | -#### 0. Any major changes? Database updates? Web server change? File structure changes? | ||
| 13 | - | ||
| 14 | -#### 1. Make backup | ||
| 15 | - | ||
| 16 | -#### 2. Stop server | ||
| 17 | - | ||
| 18 | -#### 3. Do users need to update dependencies like `git`? | ||
| 19 | - | ||
| 20 | -#### 4. Get latest code | ||
| 21 | - | ||
| 22 | -#### 5. Does GitLab shell need to be updated? | ||
| 23 | - | ||
| 24 | -#### 6. Install libs, migrations, etc. | ||
| 25 | - | ||
| 26 | -#### 7. Any config files updated since last release? | ||
| 27 | - | ||
| 28 | -Check if any of these changed since last release (~22nd of last month depending on when last release branch was created): | ||
| 29 | - | ||
| 30 | -* https://github.com/gitlabhq/gitlabhq/commits/master/lib/support/nginx/gitlab | ||
| 31 | -* https://github.com/gitlabhq/gitlab-shell/commits/master/config.yml.example | ||
| 32 | -* https://github.com/gitlabhq/gitlabhq/commits/master/config/gitlab.yml.example | ||
| 33 | -* https://github.com/gitlabhq/gitlabhq/commits/master/config/unicorn.rb.example | ||
| 34 | -* https://github.com/gitlabhq/gitlabhq/commits/master/config/database.yml.mysql | ||
| 35 | -* https://github.com/gitlabhq/gitlabhq/commits/master/config/database.yml.postgresql | ||
| 36 | - | ||
| 37 | -#### 8. Need to update init script? | ||
| 38 | - | ||
| 39 | -Check if changed since last release (~22nd of last month depending on when last release branch was created): https://github.com/gitlabhq/gitlabhq/commits/master/lib/support/init.d/gitlab | ||
| 40 | - | ||
| 41 | -#### 9. Start application | ||
| 42 | - | ||
| 43 | -#### 10. Check application status | ||
| 44 | - | ||
| 45 | -## Make sure the code quality indicatiors are good | ||
| 46 | - | ||
| 47 | -* [](http://ci.gitlab.org/projects/1?ref=master) on ci.gitlab.org (master branch) | ||
| 48 | - | ||
| 49 | -* [](https://travis-ci.org/gitlabhq/gitlabhq) on travis-ci.org (master branch) | ||
| 50 | - | ||
| 51 | -* [](https://codeclimate.com/github/gitlabhq/gitlabhq) | ||
| 52 | - | ||
| 53 | -* [](https://gemnasium.com/gitlabhq/gitlabhq) this button can be yellow (small updates are available) but must not be red (a security fix or an important update is available) | ||
| 54 | - | ||
| 55 | -* [](https://coveralls.io/r/gitlabhq/gitlabhq) | ||
| 56 | - | ||
| 57 | -## Make a release branch | ||
| 58 | - | ||
| 59 | -After making the release branch new commits are cherry-picked from master. When the release gets closer we get more selective what is cherry-picked. | ||
| 60 | - | ||
| 61 | -* 5 days before release: feature freeze | ||
| 62 | -* 3 days before release: UI freeze | ||
| 63 | -* 1 day before release: code freeze | ||
| 64 | - | ||
| 65 | -# Write a blog post | ||
| 66 | - | ||
| 67 | -* Mention what GitLab is on the second line: GitLab is open source software to collaborate on code. | ||
| 68 | -* Select and thank the the Most Valuable Person (MVP) of this release. | ||
| 69 | -* Note if there are security fixes: This release fixes an important security issue and we advise everyone to upgrade as soon as possible. | ||
| 70 | - | ||
| 71 | -## Last actions | ||
| 72 | - | ||
| 73 | -1. Update VERSION and CHANGELOG | ||
| 74 | -1. Create a git tag vX.X.X | ||
| 75 | -1. Publish the blog post | ||
| 76 | -1. Tweet about the release |
| @@ -0,0 +1,76 @@ | @@ -0,0 +1,76 @@ | ||
| 1 | +# Things to do when creating new monthly minor or major release | ||
| 2 | +NOTE: This is a guide for GitLab developers. If you are trying to install GitLab see the latest stable [installation guide](install/installation.md) and if you are trying to upgrade, see the [upgrade guides](update). | ||
| 3 | + | ||
| 4 | +## Install guide up to date? | ||
| 5 | + | ||
| 6 | +* References correct GitLab branch `x-x-stable` and correct GitLab shell tag? | ||
| 7 | + | ||
| 8 | +## Make upgrade guide | ||
| 9 | + | ||
| 10 | +### From x.x to x.x | ||
| 11 | + | ||
| 12 | +#### 0. Any major changes? Database updates? Web server change? File structure changes? | ||
| 13 | + | ||
| 14 | +#### 1. Make backup | ||
| 15 | + | ||
| 16 | +#### 2. Stop server | ||
| 17 | + | ||
| 18 | +#### 3. Do users need to update dependencies like `git`? | ||
| 19 | + | ||
| 20 | +#### 4. Get latest code | ||
| 21 | + | ||
| 22 | +#### 5. Does GitLab shell need to be updated? | ||
| 23 | + | ||
| 24 | +#### 6. Install libs, migrations, etc. | ||
| 25 | + | ||
| 26 | +#### 7. Any config files updated since last release? | ||
| 27 | + | ||
| 28 | +Check if any of these changed since last release (~22nd of last month depending on when last release branch was created): | ||
| 29 | + | ||
| 30 | +* https://github.com/gitlabhq/gitlabhq/commits/master/lib/support/nginx/gitlab | ||
| 31 | +* https://github.com/gitlabhq/gitlab-shell/commits/master/config.yml.example | ||
| 32 | +* https://github.com/gitlabhq/gitlabhq/commits/master/config/gitlab.yml.example | ||
| 33 | +* https://github.com/gitlabhq/gitlabhq/commits/master/config/unicorn.rb.example | ||
| 34 | +* https://github.com/gitlabhq/gitlabhq/commits/master/config/database.yml.mysql | ||
| 35 | +* https://github.com/gitlabhq/gitlabhq/commits/master/config/database.yml.postgresql | ||
| 36 | + | ||
| 37 | +#### 8. Need to update init script? | ||
| 38 | + | ||
| 39 | +Check if changed since last release (~22nd of last month depending on when last release branch was created): https://github.com/gitlabhq/gitlabhq/commits/master/lib/support/init.d/gitlab | ||
| 40 | + | ||
| 41 | +#### 9. Start application | ||
| 42 | + | ||
| 43 | +#### 10. Check application status | ||
| 44 | + | ||
| 45 | +## Make sure the code quality indicatiors are good | ||
| 46 | + | ||
| 47 | +* [](http://ci.gitlab.org/projects/1?ref=master) on ci.gitlab.org (master branch) | ||
| 48 | + | ||
| 49 | +* [](https://travis-ci.org/gitlabhq/gitlabhq) on travis-ci.org (master branch) | ||
| 50 | + | ||
| 51 | +* [](https://codeclimate.com/github/gitlabhq/gitlabhq) | ||
| 52 | + | ||
| 53 | +* [](https://gemnasium.com/gitlabhq/gitlabhq) this button can be yellow (small updates are available) but must not be red (a security fix or an important update is available) | ||
| 54 | + | ||
| 55 | +* [](https://coveralls.io/r/gitlabhq/gitlabhq) | ||
| 56 | + | ||
| 57 | +## Make a release branch | ||
| 58 | + | ||
| 59 | +After making the release branch new commits are cherry-picked from master. When the release gets closer we get more selective what is cherry-picked. | ||
| 60 | + | ||
| 61 | +* 5 days before release: feature freeze | ||
| 62 | +* 3 days before release: UI freeze | ||
| 63 | +* 1 day before release: code freeze | ||
| 64 | + | ||
| 65 | +# Write a blog post | ||
| 66 | + | ||
| 67 | +* Mention what GitLab is on the second line: GitLab is open source software to collaborate on code. | ||
| 68 | +* Select and thank the the Most Valuable Person (MVP) of this release. | ||
| 69 | +* Note if there are security fixes: This release fixes an important security issue and we advise everyone to upgrade as soon as possible. | ||
| 70 | + | ||
| 71 | +## Last actions | ||
| 72 | + | ||
| 73 | +1. Update VERSION and CHANGELOG | ||
| 74 | +1. Create a git tag vX.X.X | ||
| 75 | +1. Publish the blog post | ||
| 76 | +1. Tweet about the release |
| @@ -0,0 +1,76 @@ | @@ -0,0 +1,76 @@ | ||
| 1 | +# Things to do when doing an out-of-bound security release | ||
| 2 | +NOTE: This is a guide for GitLab developers. If you are trying to install GitLab see the latest stable [installation guide](install/installation.md) and if you are trying to upgrade, see the [upgrade guides](update). | ||
| 3 | + | ||
| 4 | +## When to do a security release | ||
| 5 | + | ||
| 6 | +Do a security release when there is a critical issue that needs to be adresses before the next monthly release. Otherwise include it in the monthly release and note there was a security fix in the release announcement. | ||
| 7 | + | ||
| 8 | +## Security vulnerability disclosure | ||
| 9 | + | ||
| 10 | +Please report suspected security vulnerabilities in private to support@gitlab.com, also see the [disclosure section on the GitLab.com website](http://www.gitlab.com/disclosure/). Please do NOT create publicly viewable issues for suspected security vulnerabilities. | ||
| 11 | + | ||
| 12 | +## Release Procedure | ||
| 13 | + | ||
| 14 | +1. Verify that the issue can be repoduced | ||
| 15 | +1. Acknowledge the issue to the researcher that disclosed it | ||
| 16 | +1. Fix the issue on a feature branch, do this on the private GitLab development server and update the VERSION and CHANGELOG in this branch | ||
| 17 | +1. Consider creating and testing workarounds | ||
| 18 | +1. Create feature branches for the blog posts on GitLab.org and GitLab.com and link them from the code branch | ||
| 19 | +1. Merge the code feature branch into master | ||
| 20 | +1. Cherry-pick the code into the latest stable branch | ||
| 21 | +1. Create a git tag vX.X.X for CE and another patch release for EE | ||
| 22 | +1. Push the code and the tags to all the CE and EE repositories | ||
| 23 | +1. Apply the patch to GitLab Cloud and the private GitLab development server | ||
| 24 | +1. Merge and publish the blog posts | ||
| 25 | +1. Send tweets about the release from @gitlabhq and @git_lab | ||
| 26 | +1. Send out an email to the subscribers mailing list on MailChimp | ||
| 27 | +1. Send out an email to [the community google mailing list](https://groups.google.com/forum/#!forum/gitlabhq) | ||
| 28 | +1. Send out an email to [the GitLab newsletter list](http://gitlab.us5.list-manage.com/subscribe?u=498dccd07cf3e9482bee33ba4&id=98a9a4992c) | ||
| 29 | +1. Post a signed copy of our complete announcement to [oss-security](http://www.openwall.com/lists/oss-security/) and request a CVE number | ||
| 30 | +1. Add the security researcher to the [Security Researcher Acknowledgments list](http://www.gitlab.com/vulnerability-acknowledgements/) | ||
| 31 | +1. Thank the security researcher in an email for their cooperation | ||
| 32 | +1. Update the blogpost and the CHANGELOG when we receive the CVE number | ||
| 33 | + | ||
| 34 | +The timing of the code merge into master should be coordinated in advance. | ||
| 35 | +After the merge we strive to publish the announcements within 60 minutes. | ||
| 36 | + | ||
| 37 | +## Blog post template | ||
| 38 | + | ||
| 39 | +XXX Security Advisory for GitLab | ||
| 40 | + | ||
| 41 | +A recently discovered critical vulnerability in GitLab allows [unauthenticated API access|remote code execution|unauthorized access to repositories|XXX|PICKSOMETHING]. All users should update GitLab and gitlab-shell immediately. | ||
| 42 | +We [have|haven't|XXX|PICKSOMETHING|] heard of this vulnerability being actively exploited. | ||
| 43 | + | ||
| 44 | +### Version affected | ||
| 45 | + | ||
| 46 | +GitLab Community Edition XXX and lower | ||
| 47 | +GitLab Enterprise Edition XXX and lower | ||
| 48 | + | ||
| 49 | +### Fixed versions | ||
| 50 | + | ||
| 51 | +GitLab Community Edition XXX and up | ||
| 52 | +GitLab Enterprise Edition XXX and up | ||
| 53 | + | ||
| 54 | +### Impact | ||
| 55 | + | ||
| 56 | +On GitLab installations which use MySQL as their database backend it is possible for an attacker to assume the identity of any existing GitLab user in certain API calls. This attack can be performed by [unauthenticated|authenticated|XXX|PICKSOMETHING] users. | ||
| 57 | + | ||
| 58 | +### Workarounds | ||
| 59 | + | ||
| 60 | +If you are unable to upgrade you should apply the following patch and restart GitLab. | ||
| 61 | + | ||
| 62 | +XXX | ||
| 63 | + | ||
| 64 | +### Credit | ||
| 65 | + | ||
| 66 | +We want to thank XXX of XXX for the reponsible disclosure of this vulnerability. | ||
| 67 | + | ||
| 68 | +## Email template | ||
| 69 | + | ||
| 70 | +We just announced a security advisory for GitLab at XXX | ||
| 71 | + | ||
| 72 | +Please contact us at support@gitlab.com if you have any questions. | ||
| 73 | + | ||
| 74 | +## Tweet template | ||
| 75 | + | ||
| 76 | +We just announced a security advisory for GitLab at XXX |
doc/update/5.1-to-5.4.md
| @@ -31,7 +31,7 @@ sudo -u git -H git checkout 5-4-stable # Latest version of 5-4-stable addresses | @@ -31,7 +31,7 @@ sudo -u git -H git checkout 5-4-stable # Latest version of 5-4-stable addresses | ||
| 31 | ```bash | 31 | ```bash |
| 32 | cd /home/git/gitlab-shell | 32 | cd /home/git/gitlab-shell |
| 33 | sudo -u git -H git fetch | 33 | sudo -u git -H git fetch |
| 34 | -sudo -u git -H git checkout v1.7.4 # Addresses CVE-2013-4490 | 34 | +sudo -u git -H git checkout v1.7.9 # Addresses multiple critical security vulnerabilities |
| 35 | ``` | 35 | ``` |
| 36 | 36 | ||
| 37 | ### 4. Install libs, migrations, etc. | 37 | ### 4. Install libs, migrations, etc. |
doc/update/5.1-to-6.0.md
| @@ -47,7 +47,7 @@ sudo -u git -H git checkout 6-0-stable | @@ -47,7 +47,7 @@ sudo -u git -H git checkout 6-0-stable | ||
| 47 | ```bash | 47 | ```bash |
| 48 | cd /home/git/gitlab-shell | 48 | cd /home/git/gitlab-shell |
| 49 | sudo -u git -H git fetch | 49 | sudo -u git -H git fetch |
| 50 | -sudo -u git -H git checkout v1.7.0 | 50 | +sudo -u git -H git checkout v1.7.9 |
| 51 | ``` | 51 | ``` |
| 52 | 52 | ||
| 53 | ### 4. Install additional packages | 53 | ### 4. Install additional packages |
doc/update/5.3-to-5.4.md
| @@ -30,7 +30,7 @@ sudo -u git -H git checkout 5-4-stable # Latest version of 5-4-stable addresses | @@ -30,7 +30,7 @@ sudo -u git -H git checkout 5-4-stable # Latest version of 5-4-stable addresses | ||
| 30 | ```bash | 30 | ```bash |
| 31 | cd /home/git/gitlab-shell | 31 | cd /home/git/gitlab-shell |
| 32 | sudo -u git -H git fetch | 32 | sudo -u git -H git fetch |
| 33 | -sudo -u git -H git checkout v1.7.4 # Addresses CVE-2013-4490 | 33 | +sudo -u git -H git checkout v1.7.9 # Addresses multiple critical security vulnerabilities |
| 34 | ``` | 34 | ``` |
| 35 | 35 | ||
| 36 | ### 4. Install libs, migrations, etc. | 36 | ### 4. Install libs, migrations, etc. |
doc/update/5.4-to-6.0.md
| @@ -47,7 +47,7 @@ sudo -u git -H git checkout 6-0-stable | @@ -47,7 +47,7 @@ sudo -u git -H git checkout 6-0-stable | ||
| 47 | ```bash | 47 | ```bash |
| 48 | cd /home/git/gitlab-shell | 48 | cd /home/git/gitlab-shell |
| 49 | sudo -u git -H git fetch | 49 | sudo -u git -H git fetch |
| 50 | -sudo -u git -H git checkout v1.7.0 | 50 | +sudo -u git -H git checkout v1.7.9 |
| 51 | ``` | 51 | ``` |
| 52 | 52 | ||
| 53 | ### 4. Install additional packages | 53 | ### 4. Install additional packages |
doc/update/6.0-to-6.1.md
| @@ -39,7 +39,7 @@ sudo -u git -H git checkout 6-1-stable | @@ -39,7 +39,7 @@ sudo -u git -H git checkout 6-1-stable | ||
| 39 | ```bash | 39 | ```bash |
| 40 | cd /home/git/gitlab-shell | 40 | cd /home/git/gitlab-shell |
| 41 | sudo -u git -H git fetch | 41 | sudo -u git -H git fetch |
| 42 | -sudo -u git -H git checkout v1.7.4 | 42 | +sudo -u git -H git checkout v1.7.9 |
| 43 | ``` | 43 | ``` |
| 44 | 44 | ||
| 45 | ### 4. Install libs, migrations, etc. | 45 | ### 4. Install libs, migrations, etc. |
doc/update/6.0-to-6.2.md
| @@ -47,7 +47,7 @@ sudo apt-get install logrotate | @@ -47,7 +47,7 @@ sudo apt-get install logrotate | ||
| 47 | ```bash | 47 | ```bash |
| 48 | cd /home/git/gitlab-shell | 48 | cd /home/git/gitlab-shell |
| 49 | sudo -u git -H git fetch | 49 | sudo -u git -H git fetch |
| 50 | -sudo -u git -H git checkout v1.7.4 # Addresses CVE-2013-4490 | 50 | +sudo -u git -H git checkout v1.7.9 # Addresses multiple critical security vulnerabilities |
| 51 | ``` | 51 | ``` |
| 52 | 52 | ||
| 53 | ### 5. Install libs, migrations, etc. | 53 | ### 5. Install libs, migrations, etc. |
| @@ -74,7 +74,7 @@ sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production | @@ -74,7 +74,7 @@ sudo -u git -H bundle exec rake cache:clear RAILS_ENV=production | ||
| 74 | TIP: to see what changed in gitlab.yml.example in this release use next command: | 74 | TIP: to see what changed in gitlab.yml.example in this release use next command: |
| 75 | 75 | ||
| 76 | ``` | 76 | ``` |
| 77 | -git diff 6-1-stable:config/gitlab.yml.example 6-2-stable:config/gitlab.yml.example | 77 | +git diff 6-0-stable:config/gitlab.yml.example 6-2-stable:config/gitlab.yml.example |
| 78 | ``` | 78 | ``` |
| 79 | 79 | ||
| 80 | * Make `/home/git/gitlab/config/gitlab.yml` same as https://github.com/gitlabhq/gitlabhq/blob/6-2-stable/config/gitlab.yml.example but with your settings. | 80 | * Make `/home/git/gitlab/config/gitlab.yml` same as https://github.com/gitlabhq/gitlabhq/blob/6-2-stable/config/gitlab.yml.example but with your settings. |
doc/update/6.1-to-6.2.md
| @@ -32,7 +32,7 @@ sudo -u git -H git checkout 6-2-stable # Latest version of 6-2-stable addresses | @@ -32,7 +32,7 @@ sudo -u git -H git checkout 6-2-stable # Latest version of 6-2-stable addresses | ||
| 32 | ```bash | 32 | ```bash |
| 33 | cd /home/git/gitlab-shell | 33 | cd /home/git/gitlab-shell |
| 34 | sudo -u git -H git fetch | 34 | sudo -u git -H git fetch |
| 35 | -sudo -u git -H git checkout v1.7.4 # Addresses CVE-2013-4490 | 35 | +sudo -u git -H git checkout v1.7.9 # Addresses multiple critical security vulnerabilities |
| 36 | ``` | 36 | ``` |
| 37 | 37 | ||
| 38 | ### 4. Install additional packages | 38 | ### 4. Install additional packages |
| @@ -0,0 +1,103 @@ | @@ -0,0 +1,103 @@ | ||
| 1 | +# From 6.2 to 6.3 | ||
| 2 | + | ||
| 3 | +## Requires version: 6.1 or 6.2 | ||
| 4 | + | ||
| 5 | +### 0. Backup | ||
| 6 | + | ||
| 7 | +It's useful to make a backup just in case things go south: | ||
| 8 | +(With MySQL, this may require granting "LOCK TABLES" privileges to the GitLab user on the database version) | ||
| 9 | + | ||
| 10 | +```bash | ||
| 11 | +cd /home/git/gitlab | ||
| 12 | +sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production | ||
| 13 | +``` | ||
| 14 | + | ||
| 15 | +### 1. Stop server | ||
| 16 | + | ||
| 17 | + sudo service gitlab stop | ||
| 18 | + | ||
| 19 | +### 2. Get latest code | ||
| 20 | + | ||
| 21 | +```bash | ||
| 22 | +cd /home/git/gitlab | ||
| 23 | +sudo -u git -H git fetch | ||
| 24 | +sudo -u git -H git checkout 6-3-stable | ||
| 25 | +``` | ||
| 26 | + | ||
| 27 | +### 3. Update gitlab-shell (and its config) | ||
| 28 | + | ||
| 29 | +```bash | ||
| 30 | +cd /home/git/gitlab-shell | ||
| 31 | +sudo -u git -H git fetch | ||
| 32 | +sudo -u git -H git checkout v1.7.9 # Addresses multiple critical security vulnerabilities | ||
| 33 | +``` | ||
| 34 | + | ||
| 35 | +The Gitlab-shell config changed recently, so check for config file changes and make `/home/git/gitlab-shell/config.yml` the same as https://github.com/gitlabhq/gitlab-shell/blob/master/config.yml.example | ||
| 36 | + | ||
| 37 | +### 4. Install libs, migrations, etc. | ||
| 38 | + | ||
| 39 | +```bash | ||
| 40 | +cd /home/git/gitlab | ||
| 41 | + | ||
| 42 | +# MySQL | ||
| 43 | +sudo -u git -H bundle install --without development test postgres --deployment | ||
| 44 | + | ||
| 45 | +# PostgreSQL | ||
| 46 | +sudo -u git -H bundle install --without development test mysql --deployment | ||
| 47 | + | ||
| 48 | + | ||
| 49 | +# Run database migrations | ||
| 50 | +sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production | ||
| 51 | + | ||
| 52 | +# Clean up assets and cache | ||
| 53 | +sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production | ||
| 54 | +``` | ||
| 55 | + | ||
| 56 | +### 5. Update config files | ||
| 57 | + | ||
| 58 | +TIP: to see what changed in gitlab.yml.example in this release use next command: | ||
| 59 | + | ||
| 60 | +``` | ||
| 61 | +git diff 6-2-stable:config/gitlab.yml.example 6-3-stable:config/gitlab.yml.example | ||
| 62 | +``` | ||
| 63 | + | ||
| 64 | +* Make `/home/git/gitlab/config/gitlab.yml` same as https://github.com/gitlabhq/gitlabhq/blob/6-3-stable/config/gitlab.yml.example but with your settings. | ||
| 65 | +* Make `/home/git/gitlab/config/unicorn.rb` same as https://github.com/gitlabhq/gitlabhq/blob/6-3-stable/config/unicorn.rb.example but with your settings. | ||
| 66 | +* Copy rack attack middleware config | ||
| 67 | + | ||
| 68 | +### 6. Update Init script | ||
| 69 | + | ||
| 70 | +```bash | ||
| 71 | +sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab | ||
| 72 | +sudo chmod +x /etc/init.d/gitlab | ||
| 73 | +``` | ||
| 74 | + | ||
| 75 | +### 7. Start application | ||
| 76 | + | ||
| 77 | + sudo service gitlab start | ||
| 78 | + sudo service nginx restart | ||
| 79 | + | ||
| 80 | +### 8. Check application status | ||
| 81 | + | ||
| 82 | +Check if GitLab and its environment are configured correctly: | ||
| 83 | + | ||
| 84 | + sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production | ||
| 85 | + | ||
| 86 | +To make sure you didn't miss anything run a more thorough check with: | ||
| 87 | + | ||
| 88 | + sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production | ||
| 89 | + | ||
| 90 | +If all items are green, then congratulations upgrade complete! | ||
| 91 | + | ||
| 92 | +## Things went south? Revert to previous version (6.2) | ||
| 93 | + | ||
| 94 | +### 1. Revert the code to the previous version | ||
| 95 | +Follow the [`upgrade guide from 6.1 to 6.2`](6.1-to-6.2.md), except for the database migration | ||
| 96 | +(The backup is already migrated to the previous version) | ||
| 97 | + | ||
| 98 | +### 2. Restore from the backup: | ||
| 99 | + | ||
| 100 | +```bash | ||
| 101 | +cd /home/git/gitlab | ||
| 102 | +sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production | ||
| 103 | +``` |
doc/update/patch_versions.md
| 1 | -# Universal update guide for patch versions. Ex. from From 6.2.0 to 6.2.1 | 1 | +# Universal update guide for patch versions. For example from 6.2.0 to 6.2.1, also see the [semantic versioning specification](http://semver.org/). |
| 2 | 2 | ||
| 3 | ### 0. Backup | 3 | ### 0. Backup |
| 4 | 4 | ||
| @@ -14,21 +14,25 @@ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production | @@ -14,21 +14,25 @@ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production | ||
| 14 | 14 | ||
| 15 | sudo service gitlab stop | 15 | sudo service gitlab stop |
| 16 | 16 | ||
| 17 | -### 2. Get latest code for your current stable branch | 17 | +### 2. Get latest code for the stable branch |
| 18 | 18 | ||
| 19 | ```bash | 19 | ```bash |
| 20 | cd /home/git/gitlab | 20 | cd /home/git/gitlab |
| 21 | -sudo -u git -H git pull origin 6-2-stable | 21 | +sudo -u git -H git pull origin STABLE_BRANCH |
| 22 | ``` | 22 | ``` |
| 23 | 23 | ||
| 24 | -### 3. Update gitlab-shell if necessary | 24 | +Replace STABLE_BRANCH with the minor version you want to upgrade to, for example `6-3-stable`. |
| 25 | + | ||
| 26 | +### 3. Update gitlab-shell if it is not the latest version | ||
| 25 | 27 | ||
| 26 | ```bash | 28 | ```bash |
| 27 | cd /home/git/gitlab-shell | 29 | cd /home/git/gitlab-shell |
| 28 | sudo -u git -H git fetch | 30 | sudo -u git -H git fetch |
| 29 | -sudo -u git -H git checkout v1.7.4 | 31 | +sudo -u git -H git checkout LATEST_TAG |
| 30 | ``` | 32 | ``` |
| 31 | 33 | ||
| 34 | +Replace LATEST_TAG with the latest GitLab Shell tag you want to upgrade to, for example `v1.7.9`. | ||
| 35 | + | ||
| 32 | ### 4. Install libs, migrations, etc. | 36 | ### 4. Install libs, migrations, etc. |
| 33 | 37 | ||
| 34 | ```bash | 38 | ```bash |
| @@ -0,0 +1,54 @@ | @@ -0,0 +1,54 @@ | ||
| 1 | +# Updating Ruby from source | ||
| 2 | + | ||
| 3 | +This guide explains how to update Ruby in case you installed it from source according to the instructions in https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/installation.md#2-ruby . | ||
| 4 | + | ||
| 5 | +### 1. Look for Ruby versions | ||
| 6 | +This guide will only update `/usr/local/bin/ruby`. You can see which Ruby binaries are installed on your system by running: | ||
| 7 | + | ||
| 8 | +```bash | ||
| 9 | +ls -l $(which -a ruby) | ||
| 10 | +``` | ||
| 11 | + | ||
| 12 | +### 2. Stop GitLab | ||
| 13 | + | ||
| 14 | +```bash | ||
| 15 | +sudo service gitlab stop | ||
| 16 | +``` | ||
| 17 | + | ||
| 18 | +### 3. Install or update dependencies | ||
| 19 | +Here we are assuming you are using Debian/Ubuntu. | ||
| 20 | + | ||
| 21 | +```bash | ||
| 22 | +sudo apt-get install build-essential zlib1g-dev libyaml-dev libssl-dev libgdbm-dev libreadline-dev libncurses5-dev libffi-dev curl | ||
| 23 | +``` | ||
| 24 | + | ||
| 25 | +### 4. Download, compile and install Ruby | ||
| 26 | +Find the latest stable version of Ruby 1.9 or 2.0 at https://www.ruby-lang.org/en/downloads/ . We recommend at least 2.0.0-p353, which is patched against [CVE-2013-4164](https://www.ruby-lang.org/en/news/2013/11/22/heap-overflow-in-floating-point-parsing-cve-2013-4164/). | ||
| 27 | + | ||
| 28 | +```bash | ||
| 29 | +cd /tmp | ||
| 30 | +curl --progress http://cache.ruby-lang.org/pub/ruby/2.0/ruby-2.0.0-p353.tar.gz | tar xz | ||
| 31 | +cd ruby-2.0.0-p353 | ||
| 32 | +./configure --disable-install-rdoc | ||
| 33 | +make | ||
| 34 | +sudo make install # overwrite the existing Ruby in /usr/local/bin | ||
| 35 | +sudo gem install bundler | ||
| 36 | +``` | ||
| 37 | + | ||
| 38 | +### 5. Reinstall GitLab gem bundle | ||
| 39 | +Just to be sure we will reinstall the gems used by GitLab. Note that the `bundle install` command [depends on your choice of database](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/installation.md#install-gems). | ||
| 40 | + | ||
| 41 | +```bash | ||
| 42 | +cd /home/git/gitlab | ||
| 43 | +sudo -u git -H rm -rf vendor/bundle # remove existing Gem bundle | ||
| 44 | +sudo -u git -H bundle install --deployment --without development test postgres aws # Assuming MySQL | ||
| 45 | +``` | ||
| 46 | + | ||
| 47 | +### 6. Start GitLab | ||
| 48 | +We are now ready to restart GitLab. | ||
| 49 | + | ||
| 50 | +```bash | ||
| 51 | +sudo service gitlab start | ||
| 52 | +``` | ||
| 53 | + | ||
| 54 | +### Done |
features/admin/active_tab.feature
| @@ -27,6 +27,11 @@ Feature: Admin active tab | @@ -27,6 +27,11 @@ Feature: Admin active tab | ||
| 27 | Then the active main tab should be Logs | 27 | Then the active main tab should be Logs |
| 28 | And no other main tabs should be active | 28 | And no other main tabs should be active |
| 29 | 29 | ||
| 30 | + Scenario: On Admin Messages | ||
| 31 | + Given I visit admin messages page | ||
| 32 | + Then the active main tab should be Messages | ||
| 33 | + And no other main tabs should be active | ||
| 34 | + | ||
| 30 | Scenario: On Admin Hooks | 35 | Scenario: On Admin Hooks |
| 31 | Given I visit admin hooks page | 36 | Given I visit admin hooks page |
| 32 | Then the active main tab should be Hooks | 37 | Then the active main tab should be Hooks |
| @@ -0,0 +1,13 @@ | @@ -0,0 +1,13 @@ | ||
| 1 | +Feature: Admin Broadcast Messages | ||
| 2 | + Background: | ||
| 3 | + Given I sign in as an admin | ||
| 4 | + And application already has admin messages | ||
| 5 | + And I visit admin messages page | ||
| 6 | + | ||
| 7 | + Scenario: See broadcast messages list | ||
| 8 | + Then I should be all broadcast messages | ||
| 9 | + | ||
| 10 | + Scenario: Create a broadcast message | ||
| 11 | + When submit form with new broadcast message | ||
| 12 | + Then I should be redirected to admin messages page | ||
| 13 | + And I should see newly created broadcast message |
features/profile/profile.feature
| @@ -26,6 +26,14 @@ Feature: Profile | @@ -26,6 +26,14 @@ Feature: Profile | ||
| 26 | Given I visit profile page | 26 | Given I visit profile page |
| 27 | Then I change my avatar | 27 | Then I change my avatar |
| 28 | And I should see new avatar | 28 | And I should see new avatar |
| 29 | + And I should see the "Remove avatar" button | ||
| 30 | + | ||
| 31 | + Scenario: I remove my avatar | ||
| 32 | + Given I visit profile page | ||
| 33 | + And I have an avatar | ||
| 34 | + When I remove my avatar | ||
| 35 | + Then I should see my gravatar | ||
| 36 | + And I should not see the "Remove avatar" button | ||
| 29 | 37 | ||
| 30 | Scenario: My password is expired | 38 | Scenario: My password is expired |
| 31 | Given my password is expired | 39 | Given my password is expired |
features/project/commits/commits.feature
| @@ -14,6 +14,12 @@ Feature: Project Browse commits | @@ -14,6 +14,12 @@ Feature: Project Browse commits | ||
| 14 | Scenario: I browse commit from list | 14 | Scenario: I browse commit from list |
| 15 | Given I click on commit link | 15 | Given I click on commit link |
| 16 | Then I see commit info | 16 | Then I see commit info |
| 17 | + And I see side-by-side diff button | ||
| 18 | + | ||
| 19 | + Scenario: I browse commit with side-by-side diff view | ||
| 20 | + Given I click on commit link | ||
| 21 | + And I click side-by-side diff button | ||
| 22 | + Then I see inline diff button | ||
| 17 | 23 | ||
| 18 | Scenario: I compare refs | 24 | Scenario: I compare refs |
| 19 | Given I visit compare refs page | 25 | Given I visit compare refs page |
features/project/network.feature
| @@ -29,11 +29,11 @@ Feature: Project Network Graph | @@ -29,11 +29,11 @@ Feature: Project Network Graph | ||
| 29 | @javascript | 29 | @javascript |
| 30 | Scenario: I should filter selected tag | 30 | Scenario: I should filter selected tag |
| 31 | When I switch ref to "v2.1.0" | 31 | When I switch ref to "v2.1.0" |
| 32 | - Then page should have content not cotaining "v2.1.0" | 32 | + Then page should have content not containing "v2.1.0" |
| 33 | When click "Show only selected branch" checkbox | 33 | When click "Show only selected branch" checkbox |
| 34 | - Then page should not have content not cotaining "v2.1.0" | 34 | + Then page should not have content not containing "v2.1.0" |
| 35 | When click "Show only selected branch" checkbox | 35 | When click "Show only selected branch" checkbox |
| 36 | - Then page should have content not cotaining "v2.1.0" | 36 | + Then page should have content not containing "v2.1.0" |
| 37 | 37 | ||
| 38 | Scenario: I should fail to look for a commit | 38 | Scenario: I should fail to look for a commit |
| 39 | When I look for a commit by ";" | 39 | When I look for a commit by ";" |
| @@ -0,0 +1,26 @@ | @@ -0,0 +1,26 @@ | ||
| 1 | +Feature: Project Redirects | ||
| 2 | + Background: | ||
| 3 | + Given public project "Community" | ||
| 4 | + And private project "Enterprise" | ||
| 5 | + | ||
| 6 | + Scenario: I visit public project page | ||
| 7 | + When I visit project "Community" page | ||
| 8 | + Then I should see project "Community" home page | ||
| 9 | + | ||
| 10 | + Scenario: I visit private project page | ||
| 11 | + When I visit project "Enterprise" page | ||
| 12 | + Then I should be redirected to sign in page | ||
| 13 | + | ||
| 14 | + Scenario: I visit a non-existent project page | ||
| 15 | + When I visit project "CommunityDoesNotExist" page | ||
| 16 | + Then I should be redirected to sign in page | ||
| 17 | + | ||
| 18 | + Scenario: I visit a non-existent project page as user | ||
| 19 | + Given I sign in as a user | ||
| 20 | + When I visit project "CommunityDoesNotExist" page | ||
| 21 | + Then page status code should be 404 | ||
| 22 | + | ||
| 23 | + Scenario: I visit unauthorized project page as user | ||
| 24 | + Given I sign in as a user | ||
| 25 | + When I visit project "Enterprise" page | ||
| 26 | + Then page status code should be 404 |
features/project/service.feature
| @@ -30,3 +30,9 @@ Feature: Project Services | @@ -30,3 +30,9 @@ Feature: Project Services | ||
| 30 | And I click Flowdock service link | 30 | And I click Flowdock service link |
| 31 | And I fill Flowdock settings | 31 | And I fill Flowdock settings |
| 32 | Then I should see Flowdock service settings saved | 32 | Then I should see Flowdock service settings saved |
| 33 | + | ||
| 34 | + Scenario: Activate Assembla service | ||
| 35 | + When I visit project "Shop" services page | ||
| 36 | + And I click Assembla service link | ||
| 37 | + And I fill Assembla settings | ||
| 38 | + Then I should see Assembla service settings saved | ||
| 33 | \ No newline at end of file | 39 | \ No newline at end of file |
| @@ -0,0 +1,86 @@ | @@ -0,0 +1,86 @@ | ||
| 1 | +Feature: Project Multiselect Blob | ||
| 2 | + Background: | ||
| 3 | + Given I sign in as a user | ||
| 4 | + And I own project "Shop" | ||
| 5 | + And I visit project source page | ||
| 6 | + And I click on "Gemfile.lock" file in repo | ||
| 7 | + | ||
| 8 | + @javascript | ||
| 9 | + Scenario: I click line 1 in file | ||
| 10 | + When I click line 1 in file | ||
| 11 | + Then I should see "L1" as URI fragment | ||
| 12 | + And I should see line 1 highlighted | ||
| 13 | + | ||
| 14 | + @javascript | ||
| 15 | + Scenario: I shift-click line 1 in file | ||
| 16 | + When I shift-click line 1 in file | ||
| 17 | + Then I should see "L1" as URI fragment | ||
| 18 | + And I should see line 1 highlighted | ||
| 19 | + | ||
| 20 | + @javascript | ||
| 21 | + Scenario: I click line 1 then click line 2 in file | ||
| 22 | + When I click line 1 in file | ||
| 23 | + Then I should see "L1" as URI fragment | ||
| 24 | + And I should see line 1 highlighted | ||
| 25 | + Then I click line 2 in file | ||
| 26 | + Then I should see "L2" as URI fragment | ||
| 27 | + And I should see line 2 highlighted | ||
| 28 | + | ||
| 29 | + @javascript | ||
| 30 | + Scenario: I click various line numbers to test multiselect | ||
| 31 | + Then I click line 1 in file | ||
| 32 | + Then I should see "L1" as URI fragment | ||
| 33 | + And I should see line 1 highlighted | ||
| 34 | + Then I shift-click line 2 in file | ||
| 35 | + Then I should see "L1-2" as URI fragment | ||
| 36 | + And I should see lines 1-2 highlighted | ||
| 37 | + Then I shift-click line 3 in file | ||
| 38 | + Then I should see "L1-3" as URI fragment | ||
| 39 | + And I should see lines 1-3 highlighted | ||
| 40 | + Then I click line 3 in file | ||
| 41 | + Then I should see "L3" as URI fragment | ||
| 42 | + And I should see line 3 highlighted | ||
| 43 | + Then I shift-click line 1 in file | ||
| 44 | + Then I should see "L1-3" as URI fragment | ||
| 45 | + And I should see lines 1-3 highlighted | ||
| 46 | + Then I shift-click line 5 in file | ||
| 47 | + Then I should see "L1-5" as URI fragment | ||
| 48 | + And I should see lines 1-5 highlighted | ||
| 49 | + Then I shift-click line 4 in file | ||
| 50 | + Then I should see "L1-4" as URI fragment | ||
| 51 | + And I should see lines 1-4 highlighted | ||
| 52 | + Then I click line 5 in file | ||
| 53 | + Then I should see "L5" as URI fragment | ||
| 54 | + And I should see line 5 highlighted | ||
| 55 | + Then I shift-click line 3 in file | ||
| 56 | + Then I should see "L3-5" as URI fragment | ||
| 57 | + And I should see lines 3-5 highlighted | ||
| 58 | + Then I shift-click line 1 in file | ||
| 59 | + Then I should see "L1-3" as URI fragment | ||
| 60 | + And I should see lines 1-3 highlighted | ||
| 61 | + Then I shift-click line 1 in file | ||
| 62 | + Then I should see "L1" as URI fragment | ||
| 63 | + And I should see line 1 highlighted | ||
| 64 | + | ||
| 65 | + @javascript | ||
| 66 | + Scenario: I multiselect lines 1-5 and then go back and forward in history | ||
| 67 | + When I click line 1 in file | ||
| 68 | + And I shift-click line 3 in file | ||
| 69 | + And I shift-click line 2 in file | ||
| 70 | + And I shift-click line 5 in file | ||
| 71 | + Then I should see "L1-5" as URI fragment | ||
| 72 | + And I should see lines 1-5 highlighted | ||
| 73 | + Then I go back in history | ||
| 74 | + Then I should see "L1-2" as URI fragment | ||
| 75 | + And I should see lines 1-2 highlighted | ||
| 76 | + Then I go back in history | ||
| 77 | + Then I should see "L1-3" as URI fragment | ||
| 78 | + And I should see lines 1-3 highlighted | ||
| 79 | + Then I go back in history | ||
| 80 | + Then I should see "L1" as URI fragment | ||
| 81 | + And I should see line 1 highlighted | ||
| 82 | + Then I go forward in history | ||
| 83 | + And I go forward in history | ||
| 84 | + And I go forward in history | ||
| 85 | + Then I should see "L1-5" as URI fragment | ||
| 86 | + And I should see lines 1-5 highlighted | ||
| 0 | \ No newline at end of file | 87 | \ No newline at end of file |
features/public/public_projects.feature
| 1 | Feature: Public Projects Feature | 1 | Feature: Public Projects Feature |
| 2 | Background: | 2 | Background: |
| 3 | Given public project "Community" | 3 | Given public project "Community" |
| 4 | + And internal project "Internal" | ||
| 4 | And private project "Enterprise" | 5 | And private project "Enterprise" |
| 5 | 6 | ||
| 6 | Scenario: I visit public area | 7 | Scenario: I visit public area |
| 7 | When I visit the public projects area | 8 | When I visit the public projects area |
| 8 | Then I should see project "Community" | 9 | Then I should see project "Community" |
| 10 | + And I should not see project "Internal" | ||
| 9 | And I should not see project "Enterprise" | 11 | And I should not see project "Enterprise" |
| 10 | 12 | ||
| 11 | Scenario: I visit public project page | 13 | Scenario: I visit public project page |
| 12 | When I visit project "Community" page | 14 | When I visit project "Community" page |
| 13 | Then I should see project "Community" home page | 15 | Then I should see project "Community" home page |
| 14 | 16 | ||
| 17 | + Scenario: I visit internal project page | ||
| 18 | + When I visit project "Internal" page | ||
| 19 | + Then I should be redirected to sign in page | ||
| 20 | + | ||
| 21 | + Scenario: I visit private project page | ||
| 22 | + When I visit project "Enterprise" page | ||
| 23 | + Then I should be redirected to sign in page | ||
| 24 | + | ||
| 15 | Scenario: I visit an empty public project page | 25 | Scenario: I visit an empty public project page |
| 16 | Given public empty project "Empty Public Project" | 26 | Given public empty project "Empty Public Project" |
| 17 | When I visit empty project page | 27 | When I visit empty project page |
| 18 | Then I should see empty public project details | 28 | Then I should see empty public project details |
| 29 | + | ||
| 30 | + Scenario: I visit public area as user | ||
| 31 | + Given I sign in as a user | ||
| 32 | + When I visit the public projects area | ||
| 33 | + Then I should see project "Community" | ||
| 34 | + And I should see project "Internal" | ||
| 35 | + And I should not see project "Enterprise" | ||
| 36 | + | ||
| 37 | + Scenario: I visit internal project page as user | ||
| 38 | + Given I sign in as a user | ||
| 39 | + When I visit project "Internal" page | ||
| 40 | + Then I should see project "Internal" home page | ||
| 41 | + | ||
| 42 | + Scenario: I visit public project page | ||
| 43 | + When I visit project "Community" page | ||
| 44 | + Then I should see project "Community" home page | ||
| 45 | + And I should see a http link to the repository | ||
| 46 | + | ||
| 47 | + Scenario: I visit public area as user | ||
| 48 | + Given I sign in as a user | ||
| 49 | + When I visit project "Community" page | ||
| 50 | + Then I should see project "Community" home page | ||
| 51 | + And I should see a ssh link to the repository |
features/steps/admin/admin_active_tab.rb
| @@ -30,4 +30,8 @@ class AdminActiveTab < Spinach::FeatureSteps | @@ -30,4 +30,8 @@ class AdminActiveTab < Spinach::FeatureSteps | ||
| 30 | Then 'the active main tab should be Resque' do | 30 | Then 'the active main tab should be Resque' do |
| 31 | ensure_active_main_tab('Background Jobs') | 31 | ensure_active_main_tab('Background Jobs') |
| 32 | end | 32 | end |
| 33 | + | ||
| 34 | + Then 'the active main tab should be Messages' do | ||
| 35 | + ensure_active_main_tab('Messages') | ||
| 36 | + end | ||
| 33 | end | 37 | end |
| @@ -0,0 +1,27 @@ | @@ -0,0 +1,27 @@ | ||
| 1 | +class Spinach::Features::AdminBroadcastMessages < Spinach::FeatureSteps | ||
| 2 | + include SharedAuthentication | ||
| 3 | + include SharedPaths | ||
| 4 | + include SharedAdmin | ||
| 5 | + | ||
| 6 | + step 'application already has admin messages' do | ||
| 7 | + FactoryGirl.create(:broadcast_message, message: "Migration to new server") | ||
| 8 | + end | ||
| 9 | + | ||
| 10 | + step 'I should be all broadcast messages' do | ||
| 11 | + page.should have_content "Migration to new server" | ||
| 12 | + end | ||
| 13 | + | ||
| 14 | + step 'submit form with new broadcast message' do | ||
| 15 | + fill_in 'broadcast_message_message', with: 'Application update from 4:00 CST to 5:00 CST' | ||
| 16 | + select '2018', from: "broadcast_message_ends_at_1i" | ||
| 17 | + click_button "Add broadcast message" | ||
| 18 | + end | ||
| 19 | + | ||
| 20 | + step 'I should be redirected to admin messages page' do | ||
| 21 | + current_path.should == admin_broadcast_messages_path | ||
| 22 | + end | ||
| 23 | + | ||
| 24 | + step 'I should see newly created broadcast message' do | ||
| 25 | + page.should have_content 'Application update from 4:00 CST to 5:00 CST' | ||
| 26 | + end | ||
| 27 | +end |
features/steps/profile/profile.rb
| @@ -31,26 +31,49 @@ class Profile < Spinach::FeatureSteps | @@ -31,26 +31,49 @@ class Profile < Spinach::FeatureSteps | ||
| 31 | @user.avatar.url.should == "/uploads/user/avatar/#{ @user.id }/gitlab_logo.png" | 31 | @user.avatar.url.should == "/uploads/user/avatar/#{ @user.id }/gitlab_logo.png" |
| 32 | end | 32 | end |
| 33 | 33 | ||
| 34 | + step 'I should see the "Remove avatar" button' do | ||
| 35 | + page.should have_link("Remove avatar") | ||
| 36 | + end | ||
| 37 | + | ||
| 38 | + step 'I have an avatar' do | ||
| 39 | + attach_file(:user_avatar, File.join(Rails.root, 'public', 'gitlab_logo.png')) | ||
| 40 | + click_button "Save changes" | ||
| 41 | + @user.reload | ||
| 42 | + end | ||
| 43 | + | ||
| 44 | + step 'I remove my avatar' do | ||
| 45 | + click_link "Remove avatar" | ||
| 46 | + @user.reload | ||
| 47 | + end | ||
| 48 | + | ||
| 49 | + step 'I should see my gravatar' do | ||
| 50 | + @user.avatar?.should be_false | ||
| 51 | + end | ||
| 52 | + | ||
| 53 | + step 'I should not see the "Remove avatar" button' do | ||
| 54 | + page.should_not have_link("Remove avatar") | ||
| 55 | + end | ||
| 56 | + | ||
| 34 | step 'I try change my password w/o old one' do | 57 | step 'I try change my password w/o old one' do |
| 35 | within '.update-password' do | 58 | within '.update-password' do |
| 36 | - fill_in "user_password", with: "222333" | ||
| 37 | - fill_in "user_password_confirmation", with: "222333" | 59 | + fill_in "user_password", with: "22233344" |
| 60 | + fill_in "user_password_confirmation", with: "22233344" | ||
| 38 | click_button "Save" | 61 | click_button "Save" |
| 39 | end | 62 | end |
| 40 | end | 63 | end |
| 41 | 64 | ||
| 42 | step 'I change my password' do | 65 | step 'I change my password' do |
| 43 | within '.update-password' do | 66 | within '.update-password' do |
| 44 | - fill_in "user_current_password", with: "123456" | ||
| 45 | - fill_in "user_password", with: "222333" | ||
| 46 | - fill_in "user_password_confirmation", with: "222333" | 67 | + fill_in "user_current_password", with: "12345678" |
| 68 | + fill_in "user_password", with: "22233344" | ||
| 69 | + fill_in "user_password_confirmation", with: "22233344" | ||
| 47 | click_button "Save" | 70 | click_button "Save" |
| 48 | end | 71 | end |
| 49 | end | 72 | end |
| 50 | 73 | ||
| 51 | step 'I unsuccessfully change my password' do | 74 | step 'I unsuccessfully change my password' do |
| 52 | within '.update-password' do | 75 | within '.update-password' do |
| 53 | - fill_in "user_current_password", with: "123456" | 76 | + fill_in "user_current_password", with: "12345678" |
| 54 | fill_in "user_password", with: "password" | 77 | fill_in "user_password", with: "password" |
| 55 | fill_in "user_password_confirmation", with: "confirmation" | 78 | fill_in "user_password_confirmation", with: "confirmation" |
| 56 | click_button "Save" | 79 | click_button "Save" |
| @@ -65,10 +88,6 @@ class Profile < Spinach::FeatureSteps | @@ -65,10 +88,6 @@ class Profile < Spinach::FeatureSteps | ||
| 65 | page.should have_content "Password doesn't match confirmation" | 88 | page.should have_content "Password doesn't match confirmation" |
| 66 | end | 89 | end |
| 67 | 90 | ||
| 68 | - step 'I should be redirected to sign in page' do | ||
| 69 | - current_path.should == new_user_session_path | ||
| 70 | - end | ||
| 71 | - | ||
| 72 | step 'I reset my token' do | 91 | step 'I reset my token' do |
| 73 | within '.update-token' do | 92 | within '.update-token' do |
| 74 | @old_token = @user.private_token | 93 | @old_token = @user.private_token |
features/steps/project/project_browse_commits.rb
| @@ -88,4 +88,17 @@ class ProjectBrowseCommits < Spinach::FeatureSteps | @@ -88,4 +88,17 @@ class ProjectBrowseCommits < Spinach::FeatureSteps | ||
| 88 | links[0]['href'].should =~ %r{blob/bc3735004cb45cec5e0e4fa92710897a910a5957} | 88 | links[0]['href'].should =~ %r{blob/bc3735004cb45cec5e0e4fa92710897a910a5957} |
| 89 | links[1]['href'].should =~ %r{blob/cc1ba255d6c5ffdce87a357ba7ccc397a4f4026b} | 89 | links[1]['href'].should =~ %r{blob/cc1ba255d6c5ffdce87a357ba7ccc397a4f4026b} |
| 90 | end | 90 | end |
| 91 | + | ||
| 92 | + Given 'I click side-by-side diff button' do | ||
| 93 | + click_link "Side-by-side Diff" | ||
| 94 | + end | ||
| 95 | + | ||
| 96 | + Then 'I see side-by-side diff button' do | ||
| 97 | + page.should have_content "Side-by-side Diff" | ||
| 98 | + end | ||
| 99 | + | ||
| 100 | + Then 'I see inline diff button' do | ||
| 101 | + page.should have_content "Inline Diff" | ||
| 102 | + end | ||
| 103 | + | ||
| 91 | end | 104 | end |
| @@ -0,0 +1,58 @@ | @@ -0,0 +1,58 @@ | ||
| 1 | +class ProjectMultiselectBlob < Spinach::FeatureSteps | ||
| 2 | + include SharedAuthentication | ||
| 3 | + include SharedProject | ||
| 4 | + include SharedPaths | ||
| 5 | + | ||
| 6 | + class << self | ||
| 7 | + def click_line_steps(*line_numbers) | ||
| 8 | + line_numbers.each do |line_number| | ||
| 9 | + step "I click line #{line_number} in file" do | ||
| 10 | + find("#L#{line_number}").click | ||
| 11 | + end | ||
| 12 | + | ||
| 13 | + step "I shift-click line #{line_number} in file" do | ||
| 14 | + script = "$('#L#{line_number}').trigger($.Event('click', { shiftKey: true }));" | ||
| 15 | + page.evaluate_script(script) | ||
| 16 | + end | ||
| 17 | + end | ||
| 18 | + end | ||
| 19 | + | ||
| 20 | + def check_state_steps(*ranges) | ||
| 21 | + ranges.each do |range| | ||
| 22 | + fragment = range.kind_of?(Array) ? "L#{range.first}-#{range.last}" : "L#{range}" | ||
| 23 | + pluralization = range.kind_of?(Array) ? "s" : "" | ||
| 24 | + | ||
| 25 | + step "I should see \"#{fragment}\" as URI fragment" do | ||
| 26 | + URI.parse(current_url).fragment.should == fragment | ||
| 27 | + end | ||
| 28 | + | ||
| 29 | + step "I should see line#{pluralization} #{fragment[1..-1]} highlighted" do | ||
| 30 | + ids = Array(range).map { |n| "LC#{n}" } | ||
| 31 | + extra = false | ||
| 32 | + | ||
| 33 | + highlighted = all("#tree-content-holder .highlight .line.hll") | ||
| 34 | + highlighted.each do |element| | ||
| 35 | + extra ||= ids.delete(element[:id]).nil? | ||
| 36 | + end | ||
| 37 | + | ||
| 38 | + extra.should be_false and ids.should be_empty | ||
| 39 | + end | ||
| 40 | + end | ||
| 41 | + end | ||
| 42 | + end | ||
| 43 | + | ||
| 44 | + click_line_steps *Array(1..5) | ||
| 45 | + check_state_steps *Array(1..5), Array(1..2), Array(1..3), Array(1..4), Array(1..5), Array(3..5) | ||
| 46 | + | ||
| 47 | + step 'I go back in history' do | ||
| 48 | + page.evaluate_script("window.history.back()") | ||
| 49 | + end | ||
| 50 | + | ||
| 51 | + step 'I go forward in history' do | ||
| 52 | + page.evaluate_script("window.history.forward()") | ||
| 53 | + end | ||
| 54 | + | ||
| 55 | + step 'I click on "Gemfile.lock" file in repo' do | ||
| 56 | + click_link "Gemfile.lock" | ||
| 57 | + end | ||
| 58 | +end | ||
| 0 | \ No newline at end of file | 59 | \ No newline at end of file |
features/steps/project/project_network_graph.rb
| @@ -43,13 +43,13 @@ class ProjectNetworkGraph < Spinach::FeatureSteps | @@ -43,13 +43,13 @@ class ProjectNetworkGraph < Spinach::FeatureSteps | ||
| 43 | sleep 2 | 43 | sleep 2 |
| 44 | end | 44 | end |
| 45 | 45 | ||
| 46 | - Then 'page should have content not cotaining "v2.1.0"' do | 46 | + Then 'page should have content not containing "v2.1.0"' do |
| 47 | within '.network-graph' do | 47 | within '.network-graph' do |
| 48 | page.should have_content 'cleaning' | 48 | page.should have_content 'cleaning' |
| 49 | end | 49 | end |
| 50 | end | 50 | end |
| 51 | 51 | ||
| 52 | - Then 'page should not have content not cotaining "v2.1.0"' do | 52 | + Then 'page should not have content not containing "v2.1.0"' do |
| 53 | within '.network-graph' do | 53 | within '.network-graph' do |
| 54 | page.should_not have_content 'cleaning' | 54 | page.should_not have_content 'cleaning' |
| 55 | end | 55 | end |
features/steps/project/project_services.rb
| @@ -12,6 +12,7 @@ class ProjectServices < Spinach::FeatureSteps | @@ -12,6 +12,7 @@ class ProjectServices < Spinach::FeatureSteps | ||
| 12 | page.should have_content 'Campfire' | 12 | page.should have_content 'Campfire' |
| 13 | page.should have_content 'Hipchat' | 13 | page.should have_content 'Hipchat' |
| 14 | page.should have_content 'GitLab CI' | 14 | page.should have_content 'GitLab CI' |
| 15 | + page.should have_content 'Assembla' | ||
| 15 | end | 16 | end |
| 16 | 17 | ||
| 17 | And 'I click gitlab-ci service link' do | 18 | And 'I click gitlab-ci service link' do |
| @@ -72,4 +73,18 @@ class ProjectServices < Spinach::FeatureSteps | @@ -72,4 +73,18 @@ class ProjectServices < Spinach::FeatureSteps | ||
| 72 | Then 'I should see Flowdock service settings saved' do | 73 | Then 'I should see Flowdock service settings saved' do |
| 73 | find_field('Token').value.should == 'verySecret' | 74 | find_field('Token').value.should == 'verySecret' |
| 74 | end | 75 | end |
| 76 | + | ||
| 77 | + And 'I click Assembla service link' do | ||
| 78 | + click_link 'Assembla' | ||
| 79 | + end | ||
| 80 | + | ||
| 81 | + And 'I fill Assembla settings' do | ||
| 82 | + check 'Active' | ||
| 83 | + fill_in 'Token', with: 'verySecret' | ||
| 84 | + click_button 'Save' | ||
| 85 | + end | ||
| 86 | + | ||
| 87 | + Then 'I should see Assembla service settings saved' do | ||
| 88 | + find_field('Token').value.should == 'verySecret' | ||
| 89 | + end | ||
| 75 | end | 90 | end |
| @@ -0,0 +1,35 @@ | @@ -0,0 +1,35 @@ | ||
| 1 | +class Spinach::Features::ProjectRedirects < Spinach::FeatureSteps | ||
| 2 | + include SharedAuthentication | ||
| 3 | + include SharedPaths | ||
| 4 | + include SharedProject | ||
| 5 | + | ||
| 6 | + step 'public project "Community"' do | ||
| 7 | + create :project_with_code, name: 'Community', visibility_level: Gitlab::VisibilityLevel::PUBLIC | ||
| 8 | + end | ||
| 9 | + | ||
| 10 | + step 'private project "Enterprise"' do | ||
| 11 | + create :project, name: 'Enterprise' | ||
| 12 | + end | ||
| 13 | + | ||
| 14 | + step 'I visit project "Community" page' do | ||
| 15 | + project = Project.find_by_name('Community') | ||
| 16 | + visit project_path(project) | ||
| 17 | + end | ||
| 18 | + | ||
| 19 | + step 'I should see project "Community" home page' do | ||
| 20 | + within '.project-home-title' do | ||
| 21 | + page.should have_content 'Community' | ||
| 22 | + end | ||
| 23 | + end | ||
| 24 | + | ||
| 25 | + step 'I visit project "Enterprise" page' do | ||
| 26 | + project = Project.find_by_name('Enterprise') | ||
| 27 | + visit project_path(project) | ||
| 28 | + end | ||
| 29 | + | ||
| 30 | + step 'I visit project "CommunityDoesNotExist" page' do | ||
| 31 | + project = Project.find_by_name('Community') | ||
| 32 | + visit project_path(project) + 'DoesNotExist' | ||
| 33 | + end | ||
| 34 | +end | ||
| 35 | + |
features/steps/public/projects_feature.rb
| 1 | class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps | 1 | class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps |
| 2 | + include SharedAuthentication | ||
| 2 | include SharedPaths | 3 | include SharedPaths |
| 4 | + include SharedProject | ||
| 3 | 5 | ||
| 4 | step 'I should see project "Community"' do | 6 | step 'I should see project "Community"' do |
| 5 | page.should have_content "Community" | 7 | page.should have_content "Community" |
| @@ -23,11 +25,11 @@ class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps | @@ -23,11 +25,11 @@ class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps | ||
| 23 | end | 25 | end |
| 24 | 26 | ||
| 25 | step 'public project "Community"' do | 27 | step 'public project "Community"' do |
| 26 | - create :project_with_code, name: 'Community', public: true, default_branch: 'master' | 28 | + create :project_with_code, name: 'Community', visibility_level: Gitlab::VisibilityLevel::PUBLIC |
| 27 | end | 29 | end |
| 28 | 30 | ||
| 29 | step 'public empty project "Empty Public Project"' do | 31 | step 'public empty project "Empty Public Project"' do |
| 30 | - create :project, name: 'Empty Public Project', public: true | 32 | + create :project, name: 'Empty Public Project', visibility_level: Gitlab::VisibilityLevel::PUBLIC |
| 31 | end | 33 | end |
| 32 | 34 | ||
| 33 | step 'I visit empty project page' do | 35 | step 'I visit empty project page' do |
| @@ -48,16 +50,48 @@ class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps | @@ -48,16 +50,48 @@ class Spinach::Features::PublicProjectsFeature < Spinach::FeatureSteps | ||
| 48 | create :project, name: 'Enterprise' | 50 | create :project, name: 'Enterprise' |
| 49 | end | 51 | end |
| 50 | 52 | ||
| 53 | + step 'I visit project "Enterprise" page' do | ||
| 54 | + project = Project.find_by_name('Enterprise') | ||
| 55 | + visit project_path(project) | ||
| 56 | + end | ||
| 57 | + | ||
| 51 | step 'I should see project "Community" home page' do | 58 | step 'I should see project "Community" home page' do |
| 52 | within '.project-home-title' do | 59 | within '.project-home-title' do |
| 53 | page.should have_content 'Community' | 60 | page.should have_content 'Community' |
| 54 | end | 61 | end |
| 55 | end | 62 | end |
| 56 | 63 | ||
| 57 | - private | 64 | + step 'internal project "Internal"' do |
| 65 | + create :project_with_code, name: 'Internal', visibility_level: Gitlab::VisibilityLevel::INTERNAL | ||
| 66 | + end | ||
| 67 | + | ||
| 68 | + step 'I should see project "Internal"' do | ||
| 69 | + page.should have_content "Internal" | ||
| 70 | + end | ||
| 71 | + | ||
| 72 | + step 'I should not see project "Internal"' do | ||
| 73 | + page.should_not have_content "Internal" | ||
| 74 | + end | ||
| 75 | + | ||
| 76 | + step 'I visit project "Internal" page' do | ||
| 77 | + project = Project.find_by_name('Internal') | ||
| 78 | + visit project_path(project) | ||
| 79 | + end | ||
| 80 | + | ||
| 81 | + step 'I should see project "Internal" home page' do | ||
| 82 | + within '.project-home-title' do | ||
| 83 | + page.should have_content 'Internal' | ||
| 84 | + end | ||
| 85 | + end | ||
| 86 | + | ||
| 87 | + Then 'I should see a http link to the repository' do | ||
| 88 | + project = Project.find_by_name 'Community' | ||
| 89 | + page.should have_field('project_clone', with: project.http_url_to_repo) | ||
| 90 | + end | ||
| 58 | 91 | ||
| 59 | - def project | ||
| 60 | - @project ||= Project.find_by_name("Community") | 92 | + Then 'I should see a ssh link to the repository' do |
| 93 | + project = Project.find_by_name 'Community' | ||
| 94 | + page.should have_field('project_clone', with: project.url_to_repo) | ||
| 61 | end | 95 | end |
| 62 | end | 96 | end |
| 63 | 97 |
features/steps/shared/authentication.rb
| @@ -12,6 +12,10 @@ module SharedAuthentication | @@ -12,6 +12,10 @@ module SharedAuthentication | ||
| 12 | login_as :admin | 12 | login_as :admin |
| 13 | end | 13 | end |
| 14 | 14 | ||
| 15 | + step 'I should be redirected to sign in page' do | ||
| 16 | + current_path.should == new_user_session_path | ||
| 17 | + end | ||
| 18 | + | ||
| 15 | def current_user | 19 | def current_user |
| 16 | @user || User.first | 20 | @user || User.first |
| 17 | end | 21 | end |
features/steps/shared/paths.rb
| @@ -105,6 +105,10 @@ module SharedPaths | @@ -105,6 +105,10 @@ module SharedPaths | ||
| 105 | visit admin_logs_path | 105 | visit admin_logs_path |
| 106 | end | 106 | end |
| 107 | 107 | ||
| 108 | + step 'I visit admin messages page' do | ||
| 109 | + visit admin_broadcast_messages_path | ||
| 110 | + end | ||
| 111 | + | ||
| 108 | step 'I visit admin hooks page' do | 112 | step 'I visit admin hooks page' do |
| 109 | visit admin_hooks_path | 113 | visit admin_hooks_path |
| 110 | end | 114 | end |
features/steps/snippets/snippets.rb
| @@ -19,7 +19,7 @@ class SnippetsFeature < Spinach::FeatureSteps | @@ -19,7 +19,7 @@ class SnippetsFeature < Spinach::FeatureSteps | ||
| 19 | end | 19 | end |
| 20 | 20 | ||
| 21 | And 'I click link "Destroy"' do | 21 | And 'I click link "Destroy"' do |
| 22 | - click_link "Destroy" | 22 | + click_link "Remove" |
| 23 | end | 23 | end |
| 24 | 24 | ||
| 25 | And 'I submit new snippet "Personal snippet three"' do | 25 | And 'I submit new snippet "Personal snippet three"' do |
| @@ -46,7 +46,7 @@ class SnippetsFeature < Spinach::FeatureSteps | @@ -46,7 +46,7 @@ class SnippetsFeature < Spinach::FeatureSteps | ||
| 46 | end | 46 | end |
| 47 | 47 | ||
| 48 | And 'I uncheck "Private" checkbox' do | 48 | And 'I uncheck "Private" checkbox' do |
| 49 | - find(:xpath, "//input[@id='personal_snippet_private']").set true | 49 | + choose "Public" |
| 50 | click_button "Save" | 50 | click_button "Save" |
| 51 | end | 51 | end |
| 52 | 52 |
lib/api/api.rb
lib/api/entities.rb
| @@ -24,6 +24,10 @@ module API | @@ -24,6 +24,10 @@ module API | ||
| 24 | expose :id, :url, :created_at | 24 | expose :id, :url, :created_at |
| 25 | end | 25 | end |
| 26 | 26 | ||
| 27 | + class ProjectHook < Hook | ||
| 28 | + expose :project_id, :push_events, :issues_events, :merge_requests_events | ||
| 29 | + end | ||
| 30 | + | ||
| 27 | class ForkedFromProject < Grape::Entity | 31 | class ForkedFromProject < Grape::Entity |
| 28 | expose :id | 32 | expose :id |
| 29 | expose :name, :name_with_namespace | 33 | expose :name, :name_with_namespace |
| @@ -31,11 +35,13 @@ module API | @@ -31,11 +35,13 @@ module API | ||
| 31 | end | 35 | end |
| 32 | 36 | ||
| 33 | class Project < Grape::Entity | 37 | class Project < Grape::Entity |
| 34 | - expose :id, :description, :default_branch, :public, :ssh_url_to_repo, :http_url_to_repo, :web_url | 38 | + expose :id, :description, :default_branch |
| 39 | + expose :public?, as: :public | ||
| 40 | + expose :visibility_level, :ssh_url_to_repo, :http_url_to_repo, :web_url | ||
| 35 | expose :owner, using: Entities::UserBasic | 41 | expose :owner, using: Entities::UserBasic |
| 36 | expose :name, :name_with_namespace | 42 | expose :name, :name_with_namespace |
| 37 | expose :path, :path_with_namespace | 43 | expose :path, :path_with_namespace |
| 38 | - expose :issues_enabled, :merge_requests_enabled, :wall_enabled, :wiki_enabled, :snippets_enabled, :created_at, :last_activity_at, :public | 44 | + expose :issues_enabled, :merge_requests_enabled, :wall_enabled, :wiki_enabled, :snippets_enabled, :created_at, :last_activity_at |
| 39 | expose :namespace | 45 | expose :namespace |
| 40 | expose :forked_from_project, using: Entities::ForkedFromProject, :if => lambda{ | project, options | project.forked? } | 46 | expose :forked_from_project, using: Entities::ForkedFromProject, :if => lambda{ | project, options | project.forked? } |
| 41 | end | 47 | end |
| @@ -136,5 +142,9 @@ module API | @@ -136,5 +142,9 @@ module API | ||
| 136 | expose :target_id, :target_type, :author_id | 142 | expose :target_id, :target_type, :author_id |
| 137 | expose :data, :target_title | 143 | expose :data, :target_title |
| 138 | end | 144 | end |
| 145 | + | ||
| 146 | + class Namespace < Grape::Entity | ||
| 147 | + expose :id, :path, :kind | ||
| 148 | + end | ||
| 139 | end | 149 | end |
| 140 | end | 150 | end |
| @@ -0,0 +1,99 @@ | @@ -0,0 +1,99 @@ | ||
| 1 | +module API | ||
| 2 | + # Projects API | ||
| 3 | + class Files < Grape::API | ||
| 4 | + before { authenticate! } | ||
| 5 | + before { authorize! :push_code, user_project } | ||
| 6 | + | ||
| 7 | + resource :projects do | ||
| 8 | + # Create new file in repository | ||
| 9 | + # | ||
| 10 | + # Parameters: | ||
| 11 | + # file_path (optional) - The path to new file. Ex. lib/class.rb | ||
| 12 | + # branch_name (required) - The name of branch | ||
| 13 | + # content (required) - File content | ||
| 14 | + # commit_message (required) - Commit message | ||
| 15 | + # | ||
| 16 | + # Example Request: | ||
| 17 | + # POST /projects/:id/repository/files | ||
| 18 | + # | ||
| 19 | + post ":id/repository/files" do | ||
| 20 | + required_attributes! [:file_path, :branch_name, :content, :commit_message] | ||
| 21 | + attrs = attributes_for_keys [:file_path, :branch_name, :content, :commit_message] | ||
| 22 | + branch_name = attrs.delete(:branch_name) | ||
| 23 | + file_path = attrs.delete(:file_path) | ||
| 24 | + result = ::Files::CreateContext.new(user_project, current_user, attrs, branch_name, file_path).execute | ||
| 25 | + | ||
| 26 | + if result[:status] == :success | ||
| 27 | + status(201) | ||
| 28 | + | ||
| 29 | + { | ||
| 30 | + file_path: file_path, | ||
| 31 | + branch_name: branch_name | ||
| 32 | + } | ||
| 33 | + else | ||
| 34 | + render_api_error!(result[:error], 400) | ||
| 35 | + end | ||
| 36 | + end | ||
| 37 | + | ||
| 38 | + # Update existing file in repository | ||
| 39 | + # | ||
| 40 | + # Parameters: | ||
| 41 | + # file_path (optional) - The path to file. Ex. lib/class.rb | ||
| 42 | + # branch_name (required) - The name of branch | ||
| 43 | + # content (required) - File content | ||
| 44 | + # commit_message (required) - Commit message | ||
| 45 | + # | ||
| 46 | + # Example Request: | ||
| 47 | + # PUT /projects/:id/repository/files | ||
| 48 | + # | ||
| 49 | + put ":id/repository/files" do | ||
| 50 | + required_attributes! [:file_path, :branch_name, :content, :commit_message] | ||
| 51 | + attrs = attributes_for_keys [:file_path, :branch_name, :content, :commit_message] | ||
| 52 | + branch_name = attrs.delete(:branch_name) | ||
| 53 | + file_path = attrs.delete(:file_path) | ||
| 54 | + result = ::Files::UpdateContext.new(user_project, current_user, attrs, branch_name, file_path).execute | ||
| 55 | + | ||
| 56 | + if result[:status] == :success | ||
| 57 | + status(200) | ||
| 58 | + | ||
| 59 | + { | ||
| 60 | + file_path: file_path, | ||
| 61 | + branch_name: branch_name | ||
| 62 | + } | ||
| 63 | + else | ||
| 64 | + render_api_error!(result[:error], 400) | ||
| 65 | + end | ||
| 66 | + end | ||
| 67 | + | ||
| 68 | + # Delete existing file in repository | ||
| 69 | + # | ||
| 70 | + # Parameters: | ||
| 71 | + # file_path (optional) - The path to file. Ex. lib/class.rb | ||
| 72 | + # branch_name (required) - The name of branch | ||
| 73 | + # content (required) - File content | ||
| 74 | + # commit_message (required) - Commit message | ||
| 75 | + # | ||
| 76 | + # Example Request: | ||
| 77 | + # DELETE /projects/:id/repository/files | ||
| 78 | + # | ||
| 79 | + delete ":id/repository/files" do | ||
| 80 | + required_attributes! [:file_path, :branch_name, :commit_message] | ||
| 81 | + attrs = attributes_for_keys [:file_path, :branch_name, :commit_message] | ||
| 82 | + branch_name = attrs.delete(:branch_name) | ||
| 83 | + file_path = attrs.delete(:file_path) | ||
| 84 | + result = ::Files::DeleteContext.new(user_project, current_user, attrs, branch_name, file_path).execute | ||
| 85 | + | ||
| 86 | + if result[:status] == :success | ||
| 87 | + status(200) | ||
| 88 | + | ||
| 89 | + { | ||
| 90 | + file_path: file_path, | ||
| 91 | + branch_name: branch_name | ||
| 92 | + } | ||
| 93 | + else | ||
| 94 | + render_api_error!(result[:error], 400) | ||
| 95 | + end | ||
| 96 | + end | ||
| 97 | + end | ||
| 98 | + end | ||
| 99 | +end |
lib/api/helpers.rb
| @@ -6,19 +6,23 @@ module API | @@ -6,19 +6,23 @@ module API | ||
| 6 | SUDO_PARAM = :sudo | 6 | SUDO_PARAM = :sudo |
| 7 | 7 | ||
| 8 | def current_user | 8 | def current_user |
| 9 | - @current_user ||= User.find_by_authentication_token(params[PRIVATE_TOKEN_PARAM] || env[PRIVATE_TOKEN_HEADER]) | 9 | + private_token = (params[PRIVATE_TOKEN_PARAM] || env[PRIVATE_TOKEN_HEADER]).to_s |
| 10 | + @current_user ||= User.find_by_authentication_token(private_token) | ||
| 10 | identifier = sudo_identifier() | 11 | identifier = sudo_identifier() |
| 12 | + | ||
| 11 | # If the sudo is the current user do nothing | 13 | # If the sudo is the current user do nothing |
| 12 | if (identifier && !(@current_user.id == identifier || @current_user.username == identifier)) | 14 | if (identifier && !(@current_user.id == identifier || @current_user.username == identifier)) |
| 13 | render_api_error!('403 Forbidden: Must be admin to use sudo', 403) unless @current_user.is_admin? | 15 | render_api_error!('403 Forbidden: Must be admin to use sudo', 403) unless @current_user.is_admin? |
| 14 | @current_user = User.by_username_or_id(identifier) | 16 | @current_user = User.by_username_or_id(identifier) |
| 15 | not_found!("No user id or username for: #{identifier}") if @current_user.nil? | 17 | not_found!("No user id or username for: #{identifier}") if @current_user.nil? |
| 16 | end | 18 | end |
| 19 | + | ||
| 17 | @current_user | 20 | @current_user |
| 18 | end | 21 | end |
| 19 | 22 | ||
| 20 | def sudo_identifier() | 23 | def sudo_identifier() |
| 21 | identifier ||= params[SUDO_PARAM] ||= env[SUDO_HEADER] | 24 | identifier ||= params[SUDO_PARAM] ||= env[SUDO_HEADER] |
| 25 | + | ||
| 22 | # Regex for integers | 26 | # Regex for integers |
| 23 | if (!!(identifier =~ /^[0-9]+$/)) | 27 | if (!!(identifier =~ /^[0-9]+$/)) |
| 24 | identifier.to_i | 28 | identifier.to_i |
| @@ -29,6 +33,7 @@ module API | @@ -29,6 +33,7 @@ module API | ||
| 29 | 33 | ||
| 30 | def set_current_user_for_thread | 34 | def set_current_user_for_thread |
| 31 | Thread.current[:current_user] = current_user | 35 | Thread.current[:current_user] = current_user |
| 36 | + | ||
| 32 | begin | 37 | begin |
| 33 | yield | 38 | yield |
| 34 | ensure | 39 | ensure |
| @@ -0,0 +1,23 @@ | @@ -0,0 +1,23 @@ | ||
| 1 | +module API | ||
| 2 | + # namespaces API | ||
| 3 | + class Namespaces < Grape::API | ||
| 4 | + before { | ||
| 5 | + authenticate! | ||
| 6 | + authenticated_as_admin! | ||
| 7 | + } | ||
| 8 | + | ||
| 9 | + resource :namespaces do | ||
| 10 | + # Get a namespaces list | ||
| 11 | + # | ||
| 12 | + # Example Request: | ||
| 13 | + # GET /namespaces | ||
| 14 | + get do | ||
| 15 | + @namespaces = Namespace.scoped | ||
| 16 | + @namespaces = @namespaces.search(params[:search]) if params[:search].present? | ||
| 17 | + @namespaces = paginate @namespaces | ||
| 18 | + | ||
| 19 | + present @namespaces, with: Entities::Namespace | ||
| 20 | + end | ||
| 21 | + end | ||
| 22 | + end | ||
| 23 | +end |
lib/api/project_hooks.rb
| @@ -22,7 +22,7 @@ module API | @@ -22,7 +22,7 @@ module API | ||
| 22 | # GET /projects/:id/hooks | 22 | # GET /projects/:id/hooks |
| 23 | get ":id/hooks" do | 23 | get ":id/hooks" do |
| 24 | @hooks = paginate user_project.hooks | 24 | @hooks = paginate user_project.hooks |
| 25 | - present @hooks, with: Entities::Hook | 25 | + present @hooks, with: Entities::ProjectHook |
| 26 | end | 26 | end |
| 27 | 27 | ||
| 28 | # Get a project hook | 28 | # Get a project hook |
| @@ -34,7 +34,7 @@ module API | @@ -34,7 +34,7 @@ module API | ||
| 34 | # GET /projects/:id/hooks/:hook_id | 34 | # GET /projects/:id/hooks/:hook_id |
| 35 | get ":id/hooks/:hook_id" do | 35 | get ":id/hooks/:hook_id" do |
| 36 | @hook = user_project.hooks.find(params[:hook_id]) | 36 | @hook = user_project.hooks.find(params[:hook_id]) |
| 37 | - present @hook, with: Entities::Hook | 37 | + present @hook, with: Entities::ProjectHook |
| 38 | end | 38 | end |
| 39 | 39 | ||
| 40 | 40 | ||
| @@ -47,10 +47,11 @@ module API | @@ -47,10 +47,11 @@ module API | ||
| 47 | # POST /projects/:id/hooks | 47 | # POST /projects/:id/hooks |
| 48 | post ":id/hooks" do | 48 | post ":id/hooks" do |
| 49 | required_attributes! [:url] | 49 | required_attributes! [:url] |
| 50 | + attrs = attributes_for_keys [:url, :push_events, :issues_events, :merge_requests_events] | ||
| 51 | + @hook = user_project.hooks.new(attrs) | ||
| 50 | 52 | ||
| 51 | - @hook = user_project.hooks.new({"url" => params[:url]}) | ||
| 52 | if @hook.save | 53 | if @hook.save |
| 53 | - present @hook, with: Entities::Hook | 54 | + present @hook, with: Entities::ProjectHook |
| 54 | else | 55 | else |
| 55 | if @hook.errors[:url].present? | 56 | if @hook.errors[:url].present? |
| 56 | error!("Invalid url given", 422) | 57 | error!("Invalid url given", 422) |
| @@ -70,10 +71,10 @@ module API | @@ -70,10 +71,10 @@ module API | ||
| 70 | put ":id/hooks/:hook_id" do | 71 | put ":id/hooks/:hook_id" do |
| 71 | @hook = user_project.hooks.find(params[:hook_id]) | 72 | @hook = user_project.hooks.find(params[:hook_id]) |
| 72 | required_attributes! [:url] | 73 | required_attributes! [:url] |
| 74 | + attrs = attributes_for_keys [:url, :push_events, :issues_events, :merge_requests_events] | ||
| 73 | 75 | ||
| 74 | - attrs = attributes_for_keys [:url] | ||
| 75 | if @hook.update_attributes attrs | 76 | if @hook.update_attributes attrs |
| 76 | - present @hook, with: Entities::Hook | 77 | + present @hook, with: Entities::ProjectHook |
| 77 | else | 78 | else |
| 78 | if @hook.errors[:url].present? | 79 | if @hook.errors[:url].present? |
| 79 | error!("Invalid url given", 422) | 80 | error!("Invalid url given", 422) |
lib/api/projects.rb
| @@ -11,6 +11,13 @@ module API | @@ -11,6 +11,13 @@ module API | ||
| 11 | end | 11 | end |
| 12 | not_found! | 12 | not_found! |
| 13 | end | 13 | end |
| 14 | + | ||
| 15 | + def map_public_to_visibility_level(attrs) | ||
| 16 | + publik = attrs.delete(:public) | ||
| 17 | + publik = [ true, 1, '1', 't', 'T', 'true', 'TRUE', 'on', 'ON' ].include?(publik) | ||
| 18 | + attrs[:visibility_level] = Gitlab::VisibilityLevel::PUBLIC if !attrs[:visibility_level].present? && publik == true | ||
| 19 | + attrs | ||
| 20 | + end | ||
| 14 | end | 21 | end |
| 15 | 22 | ||
| 16 | # Get a projects list for authenticated user | 23 | # Get a projects list for authenticated user |
| @@ -31,6 +38,16 @@ module API | @@ -31,6 +38,16 @@ module API | ||
| 31 | present @projects, with: Entities::Project | 38 | present @projects, with: Entities::Project |
| 32 | end | 39 | end |
| 33 | 40 | ||
| 41 | + # Get all projects for admin user | ||
| 42 | + # | ||
| 43 | + # Example Request: | ||
| 44 | + # GET /projects/all | ||
| 45 | + get '/all' do | ||
| 46 | + authenticated_as_admin! | ||
| 47 | + @projects = paginate Project | ||
| 48 | + present @projects, with: Entities::Project | ||
| 49 | + end | ||
| 50 | + | ||
| 34 | # Get a single project | 51 | # Get a single project |
| 35 | # | 52 | # |
| 36 | # Parameters: | 53 | # Parameters: |
| @@ -60,14 +77,14 @@ module API | @@ -60,14 +77,14 @@ module API | ||
| 60 | # Parameters: | 77 | # Parameters: |
| 61 | # name (required) - name for new project | 78 | # name (required) - name for new project |
| 62 | # description (optional) - short project description | 79 | # description (optional) - short project description |
| 63 | - # default_branch (optional) - 'master' by default | ||
| 64 | # issues_enabled (optional) | 80 | # issues_enabled (optional) |
| 65 | # wall_enabled (optional) | 81 | # wall_enabled (optional) |
| 66 | # merge_requests_enabled (optional) | 82 | # merge_requests_enabled (optional) |
| 67 | # wiki_enabled (optional) | 83 | # wiki_enabled (optional) |
| 68 | # snippets_enabled (optional) | 84 | # snippets_enabled (optional) |
| 69 | # namespace_id (optional) - defaults to user namespace | 85 | # namespace_id (optional) - defaults to user namespace |
| 70 | - # public (optional) - false by default | 86 | + # public (optional) - if true same as setting visibility_level = 20 |
| 87 | + # visibility_level (optional) - 0 by default | ||
| 71 | # Example Request | 88 | # Example Request |
| 72 | # POST /projects | 89 | # POST /projects |
| 73 | post do | 90 | post do |
| @@ -75,14 +92,15 @@ module API | @@ -75,14 +92,15 @@ module API | ||
| 75 | attrs = attributes_for_keys [:name, | 92 | attrs = attributes_for_keys [:name, |
| 76 | :path, | 93 | :path, |
| 77 | :description, | 94 | :description, |
| 78 | - :default_branch, | ||
| 79 | :issues_enabled, | 95 | :issues_enabled, |
| 80 | :wall_enabled, | 96 | :wall_enabled, |
| 81 | :merge_requests_enabled, | 97 | :merge_requests_enabled, |
| 82 | :wiki_enabled, | 98 | :wiki_enabled, |
| 83 | :snippets_enabled, | 99 | :snippets_enabled, |
| 84 | :namespace_id, | 100 | :namespace_id, |
| 85 | - :public] | 101 | + :public, |
| 102 | + :visibility_level] | ||
| 103 | + attrs = map_public_to_visibility_level(attrs) | ||
| 86 | @project = ::Projects::CreateContext.new(current_user, attrs).execute | 104 | @project = ::Projects::CreateContext.new(current_user, attrs).execute |
| 87 | if @project.saved? | 105 | if @project.saved? |
| 88 | present @project, with: Entities::Project | 106 | present @project, with: Entities::Project |
| @@ -106,7 +124,8 @@ module API | @@ -106,7 +124,8 @@ module API | ||
| 106 | # merge_requests_enabled (optional) | 124 | # merge_requests_enabled (optional) |
| 107 | # wiki_enabled (optional) | 125 | # wiki_enabled (optional) |
| 108 | # snippets_enabled (optional) | 126 | # snippets_enabled (optional) |
| 109 | - # public (optional) | 127 | + # public (optional) - if true same as setting visibility_level = 20 |
| 128 | + # visibility_level (optional) | ||
| 110 | # Example Request | 129 | # Example Request |
| 111 | # POST /projects/user/:user_id | 130 | # POST /projects/user/:user_id |
| 112 | post "user/:user_id" do | 131 | post "user/:user_id" do |
| @@ -120,7 +139,9 @@ module API | @@ -120,7 +139,9 @@ module API | ||
| 120 | :merge_requests_enabled, | 139 | :merge_requests_enabled, |
| 121 | :wiki_enabled, | 140 | :wiki_enabled, |
| 122 | :snippets_enabled, | 141 | :snippets_enabled, |
| 123 | - :public] | 142 | + :public, |
| 143 | + :visibility_level] | ||
| 144 | + attrs = map_public_to_visibility_level(attrs) | ||
| 124 | @project = ::Projects::CreateContext.new(user, attrs).execute | 145 | @project = ::Projects::CreateContext.new(user, attrs).execute |
| 125 | if @project.saved? | 146 | if @project.saved? |
| 126 | present @project, with: Entities::Project | 147 | present @project, with: Entities::Project |
| @@ -282,7 +303,8 @@ module API | @@ -282,7 +303,8 @@ module API | ||
| 282 | # GET /projects/search/:query | 303 | # GET /projects/search/:query |
| 283 | get "/search/:query" do | 304 | get "/search/:query" do |
| 284 | ids = current_user.authorized_projects.map(&:id) | 305 | ids = current_user.authorized_projects.map(&:id) |
| 285 | - projects = Project.where("(id in (?) OR public = true) AND (name LIKE (?))", ids, "%#{params[:query]}%") | 306 | + visibility_levels = [ Gitlab::VisibilityLevel::INTERNAL, Gitlab::VisibilityLevel::PUBLIC ] |
| 307 | + projects = Project.where("(id in (?) OR visibility_level in (?)) AND (name LIKE (?))", ids, visibility_levels, "%#{params[:query]}%") | ||
| 286 | present paginate(projects), with: Entities::Project | 308 | present paginate(projects), with: Entities::Project |
| 287 | end | 309 | end |
| 288 | end | 310 | end |
lib/backup/database.rb
| @@ -13,20 +13,20 @@ module Backup | @@ -13,20 +13,20 @@ module Backup | ||
| 13 | def dump | 13 | def dump |
| 14 | case config["adapter"] | 14 | case config["adapter"] |
| 15 | when /^mysql/ then | 15 | when /^mysql/ then |
| 16 | - system("mysqldump #{mysql_args} #{config['database']} > #{db_file_name}") | 16 | + system('mysqldump', *mysql_args, config['database'], out: db_file_name) |
| 17 | when "postgresql" then | 17 | when "postgresql" then |
| 18 | pg_env | 18 | pg_env |
| 19 | - system("pg_dump #{config['database']} > #{db_file_name}") | 19 | + system('pg_dump', config['database'], out: db_file_name) |
| 20 | end | 20 | end |
| 21 | end | 21 | end |
| 22 | 22 | ||
| 23 | def restore | 23 | def restore |
| 24 | case config["adapter"] | 24 | case config["adapter"] |
| 25 | when /^mysql/ then | 25 | when /^mysql/ then |
| 26 | - system("mysql #{mysql_args} #{config['database']} < #{db_file_name}") | 26 | + system('mysql', *mysql_args, config['database'], in: db_file_name) |
| 27 | when "postgresql" then | 27 | when "postgresql" then |
| 28 | pg_env | 28 | pg_env |
| 29 | - system("psql #{config['database']} -f #{db_file_name}") | 29 | + system('psql', config['database'], '-f', db_file_name) |
| 30 | end | 30 | end |
| 31 | end | 31 | end |
| 32 | 32 | ||
| @@ -45,7 +45,7 @@ module Backup | @@ -45,7 +45,7 @@ module Backup | ||
| 45 | 'encoding' => '--default-character-set', | 45 | 'encoding' => '--default-character-set', |
| 46 | 'password' => '--password' | 46 | 'password' => '--password' |
| 47 | } | 47 | } |
| 48 | - args.map { |opt, arg| "#{arg}='#{config[opt]}'" if config[opt] }.compact.join(' ') | 48 | + args.map { |opt, arg| "#{arg}=#{config[opt]}" if config[opt] }.compact |
| 49 | end | 49 | end |
| 50 | 50 | ||
| 51 | def pg_env | 51 | def pg_env |
lib/backup/manager.rb
| 1 | module Backup | 1 | module Backup |
| 2 | class Manager | 2 | class Manager |
| 3 | + BACKUP_CONTENTS = %w{repositories/ db/ uploads/ backup_information.yml} | ||
| 4 | + | ||
| 3 | def pack | 5 | def pack |
| 4 | # saving additional informations | 6 | # saving additional informations |
| 5 | s = {} | 7 | s = {} |
| @@ -16,7 +18,7 @@ module Backup | @@ -16,7 +18,7 @@ module Backup | ||
| 16 | 18 | ||
| 17 | # create archive | 19 | # create archive |
| 18 | print "Creating backup archive: #{s[:backup_created_at].to_i}_gitlab_backup.tar ... " | 20 | print "Creating backup archive: #{s[:backup_created_at].to_i}_gitlab_backup.tar ... " |
| 19 | - if Kernel.system("tar -cf #{s[:backup_created_at].to_i}_gitlab_backup.tar repositories/ db/ uploads/ backup_information.yml") | 21 | + if Kernel.system('tar', '-cf', "#{s[:backup_created_at].to_i}_gitlab_backup.tar", *BACKUP_CONTENTS) |
| 20 | puts "done".green | 22 | puts "done".green |
| 21 | else | 23 | else |
| 22 | puts "failed".red | 24 | puts "failed".red |
| @@ -25,7 +27,7 @@ module Backup | @@ -25,7 +27,7 @@ module Backup | ||
| 25 | 27 | ||
| 26 | def cleanup | 28 | def cleanup |
| 27 | print "Deleting tmp directories ... " | 29 | print "Deleting tmp directories ... " |
| 28 | - if Kernel.system("rm -rf repositories/ db/ uploads/ backup_information.yml") | 30 | + if Kernel.system('rm', '-rf', *BACKUP_CONTENTS) |
| 29 | puts "done".green | 31 | puts "done".green |
| 30 | else | 32 | else |
| 31 | puts "failed".red | 33 | puts "failed".red |
| @@ -44,7 +46,7 @@ module Backup | @@ -44,7 +46,7 @@ module Backup | ||
| 44 | file_list.map! { |f| $1.to_i if f =~ /(\d+)_gitlab_backup.tar/ } | 46 | file_list.map! { |f| $1.to_i if f =~ /(\d+)_gitlab_backup.tar/ } |
| 45 | file_list.sort.each do |timestamp| | 47 | file_list.sort.each do |timestamp| |
| 46 | if Time.at(timestamp) < (Time.now - keep_time) | 48 | if Time.at(timestamp) < (Time.now - keep_time) |
| 47 | - if system("rm #{timestamp}_gitlab_backup.tar") | 49 | + if Kernel.system(*%W(rm #{timestamp}_gitlab_backup.tar)) |
| 48 | removed += 1 | 50 | removed += 1 |
| 49 | end | 51 | end |
| 50 | end | 52 | end |
| @@ -75,7 +77,7 @@ module Backup | @@ -75,7 +77,7 @@ module Backup | ||
| 75 | end | 77 | end |
| 76 | 78 | ||
| 77 | print "Unpacking backup ... " | 79 | print "Unpacking backup ... " |
| 78 | - unless Kernel.system("tar -xf #{tar_file}") | 80 | + unless Kernel.system(*%W(tar -xf #{tar_file})) |
| 79 | puts "failed".red | 81 | puts "failed".red |
| 80 | exit 1 | 82 | exit 1 |
| 81 | else | 83 | else |
lib/backup/repository.rb
| @@ -18,7 +18,7 @@ module Backup | @@ -18,7 +18,7 @@ module Backup | ||
| 18 | # Create namespace dir if missing | 18 | # Create namespace dir if missing |
| 19 | FileUtils.mkdir_p(File.join(backup_repos_path, project.namespace.path)) if project.namespace | 19 | FileUtils.mkdir_p(File.join(backup_repos_path, project.namespace.path)) if project.namespace |
| 20 | 20 | ||
| 21 | - if system("cd #{path_to_repo(project)} > /dev/null 2>&1 && git bundle create #{path_to_bundle(project)} --all > /dev/null 2>&1") | 21 | + if system(*%W(git --git-dir=#{path_to_repo(project)} bundle create #{path_to_bundle(project)} --all), silent) |
| 22 | puts "[DONE]".green | 22 | puts "[DONE]".green |
| 23 | else | 23 | else |
| 24 | puts "[FAILED]".red | 24 | puts "[FAILED]".red |
| @@ -30,7 +30,7 @@ module Backup | @@ -30,7 +30,7 @@ module Backup | ||
| 30 | print " * #{wiki.path_with_namespace} ... " | 30 | print " * #{wiki.path_with_namespace} ... " |
| 31 | if wiki.empty? | 31 | if wiki.empty? |
| 32 | puts " [SKIPPED]".cyan | 32 | puts " [SKIPPED]".cyan |
| 33 | - elsif system("cd #{path_to_repo(wiki)} > /dev/null 2>&1 && git bundle create #{path_to_bundle(wiki)} --all > /dev/null 2>&1") | 33 | + elsif system(*%W(git --git-dir=#{path_to_repo(wiki)} bundle create #{path_to_bundle(wiki)} --all), silent) |
| 34 | puts " [DONE]".green | 34 | puts " [DONE]".green |
| 35 | else | 35 | else |
| 36 | puts " [FAILED]".red | 36 | puts " [FAILED]".red |
| @@ -53,7 +53,7 @@ module Backup | @@ -53,7 +53,7 @@ module Backup | ||
| 53 | 53 | ||
| 54 | project.namespace.ensure_dir_exist if project.namespace | 54 | project.namespace.ensure_dir_exist if project.namespace |
| 55 | 55 | ||
| 56 | - if system("git clone --bare #{path_to_bundle(project)} #{path_to_repo(project)} > /dev/null 2>&1") | 56 | + if system(*%W(git clone --bare #{path_to_bundle(project)} #{path_to_repo(project)}), silent) |
| 57 | puts "[DONE]".green | 57 | puts "[DONE]".green |
| 58 | else | 58 | else |
| 59 | puts "[FAILED]".red | 59 | puts "[FAILED]".red |
| @@ -63,7 +63,7 @@ module Backup | @@ -63,7 +63,7 @@ module Backup | ||
| 63 | 63 | ||
| 64 | if File.exists?(path_to_bundle(wiki)) | 64 | if File.exists?(path_to_bundle(wiki)) |
| 65 | print " * #{wiki.path_with_namespace} ... " | 65 | print " * #{wiki.path_with_namespace} ... " |
| 66 | - if system("git clone --bare #{path_to_bundle(wiki)} #{path_to_repo(wiki)} > /dev/null 2>&1") | 66 | + if system(*%W(git clone --bare #{path_to_bundle(wiki)} #{path_to_repo(wiki)}), silent) |
| 67 | puts " [DONE]".green | 67 | puts " [DONE]".green |
| 68 | else | 68 | else |
| 69 | puts " [FAILED]".red | 69 | puts " [FAILED]".red |
| @@ -73,7 +73,7 @@ module Backup | @@ -73,7 +73,7 @@ module Backup | ||
| 73 | 73 | ||
| 74 | print 'Put GitLab hooks in repositories dirs'.yellow | 74 | print 'Put GitLab hooks in repositories dirs'.yellow |
| 75 | gitlab_shell_user_home = File.expand_path("~#{Gitlab.config.gitlab_shell.ssh_user}") | 75 | gitlab_shell_user_home = File.expand_path("~#{Gitlab.config.gitlab_shell.ssh_user}") |
| 76 | - if system("#{gitlab_shell_user_home}/gitlab-shell/support/rewrite-hooks.sh #{Gitlab.config.gitlab_shell.repos_path}") | 76 | + if system("#{gitlab_shell_user_home}/gitlab-shell/support/rewrite-hooks.sh", Gitlab.config.gitlab_shell.repos_path) |
| 77 | puts " [DONE]".green | 77 | puts " [DONE]".green |
| 78 | else | 78 | else |
| 79 | puts " [FAILED]".red | 79 | puts " [FAILED]".red |
| @@ -103,5 +103,9 @@ module Backup | @@ -103,5 +103,9 @@ module Backup | ||
| 103 | FileUtils.rm_rf(backup_repos_path) | 103 | FileUtils.rm_rf(backup_repos_path) |
| 104 | FileUtils.mkdir_p(backup_repos_path) | 104 | FileUtils.mkdir_p(backup_repos_path) |
| 105 | end | 105 | end |
| 106 | + | ||
| 107 | + def silent | ||
| 108 | + {err: '/dev/null', out: '/dev/null'} | ||
| 109 | + end | ||
| 106 | end | 110 | end |
| 107 | end | 111 | end |
lib/backup/uploads.rb
| @@ -19,7 +19,7 @@ module Backup | @@ -19,7 +19,7 @@ module Backup | ||
| 19 | 19 | ||
| 20 | FileUtils.cp_r(backup_uploads_dir, app_uploads_dir) | 20 | FileUtils.cp_r(backup_uploads_dir, app_uploads_dir) |
| 21 | end | 21 | end |
| 22 | - | 22 | + |
| 23 | def backup_existing_uploads_dir | 23 | def backup_existing_uploads_dir |
| 24 | if File.exists?(app_uploads_dir) | 24 | if File.exists?(app_uploads_dir) |
| 25 | FileUtils.mv(app_uploads_dir, Rails.root.join('public', "uploads.#{Time.now.to_i}")) | 25 | FileUtils.mv(app_uploads_dir, Rails.root.join('public', "uploads.#{Time.now.to_i}")) |
lib/gitlab/backend/grack_auth.rb
| @@ -58,7 +58,7 @@ module Grack | @@ -58,7 +58,7 @@ module Grack | ||
| 58 | end | 58 | end |
| 59 | 59 | ||
| 60 | else | 60 | else |
| 61 | - return unauthorized unless project.public | 61 | + return unauthorized unless project.public? |
| 62 | end | 62 | end |
| 63 | 63 | ||
| 64 | if authorized_git_request? | 64 | if authorized_git_request? |
| @@ -80,15 +80,19 @@ module Grack | @@ -80,15 +80,19 @@ module Grack | ||
| 80 | def authorize_request(service) | 80 | def authorize_request(service) |
| 81 | case service | 81 | case service |
| 82 | when 'git-upload-pack' | 82 | when 'git-upload-pack' |
| 83 | - project.public || can?(user, :download_code, project) | 83 | + can?(user, :download_code, project) |
| 84 | when'git-receive-pack' | 84 | when'git-receive-pack' |
| 85 | - action = if project.protected_branch?(ref) | ||
| 86 | - :push_code_to_protected_branches | ||
| 87 | - else | ||
| 88 | - :push_code | ||
| 89 | - end | 85 | + refs.each do |ref| |
| 86 | + action = if project.protected_branch?(ref) | ||
| 87 | + :push_code_to_protected_branches | ||
| 88 | + else | ||
| 89 | + :push_code | ||
| 90 | + end | ||
| 91 | + | ||
| 92 | + return false unless can?(user, action, project) | ||
| 93 | + end | ||
| 90 | 94 | ||
| 91 | - can?(user, action, project) | 95 | + true |
| 92 | else | 96 | else |
| 93 | false | 97 | false |
| 94 | end | 98 | end |
| @@ -108,11 +112,11 @@ module Grack | @@ -108,11 +112,11 @@ module Grack | ||
| 108 | @project ||= project_by_path(@request.path_info) | 112 | @project ||= project_by_path(@request.path_info) |
| 109 | end | 113 | end |
| 110 | 114 | ||
| 111 | - def ref | ||
| 112 | - @ref ||= parse_ref | 115 | + def refs |
| 116 | + @refs ||= parse_refs | ||
| 113 | end | 117 | end |
| 114 | 118 | ||
| 115 | - def parse_ref | 119 | + def parse_refs |
| 116 | input = if @env["HTTP_CONTENT_ENCODING"] =~ /gzip/ | 120 | input = if @env["HTTP_CONTENT_ENCODING"] =~ /gzip/ |
| 117 | Zlib::GzipReader.new(@request.body).read | 121 | Zlib::GzipReader.new(@request.body).read |
| 118 | else | 122 | else |
| @@ -121,7 +125,15 @@ module Grack | @@ -121,7 +125,15 @@ module Grack | ||
| 121 | 125 | ||
| 122 | # Need to reset seek point | 126 | # Need to reset seek point |
| 123 | @request.body.rewind | 127 | @request.body.rewind |
| 124 | - /refs\/heads\/([\/\w\.-]+)/n.match(input.force_encoding('ascii-8bit')).to_a.last | 128 | + |
| 129 | + # Parse refs | ||
| 130 | + refs = input.force_encoding('ascii-8bit').scan(/refs\/heads\/([\/\w\.-]+)/n).flatten.compact | ||
| 131 | + | ||
| 132 | + # Cleanup grabare from refs | ||
| 133 | + # if push to multiple branches | ||
| 134 | + refs.map do |ref| | ||
| 135 | + ref.gsub(/00.*/, "") | ||
| 136 | + end | ||
| 125 | end | 137 | end |
| 126 | end | 138 | end |
| 127 | end | 139 | end |
lib/gitlab/backend/shell.rb
| @@ -196,6 +196,15 @@ module Gitlab | @@ -196,6 +196,15 @@ module Gitlab | ||
| 196 | Gitlab.config.gitlab_shell.ssh_path_prefix + "#{path}.git" | 196 | Gitlab.config.gitlab_shell.ssh_path_prefix + "#{path}.git" |
| 197 | end | 197 | end |
| 198 | 198 | ||
| 199 | + # Return GitLab shell version | ||
| 200 | + def version | ||
| 201 | + gitlab_shell_version_file = "#{gitlab_shell_user_home}/gitlab-shell/VERSION" | ||
| 202 | + | ||
| 203 | + if File.readable?(gitlab_shell_version_file) | ||
| 204 | + File.read(gitlab_shell_version_file) | ||
| 205 | + end | ||
| 206 | + end | ||
| 207 | + | ||
| 199 | protected | 208 | protected |
| 200 | 209 | ||
| 201 | def gitlab_shell_user_home | 210 | def gitlab_shell_user_home |
lib/gitlab/ldap/user.rb
| @@ -23,8 +23,8 @@ module Gitlab | @@ -23,8 +23,8 @@ module Gitlab | ||
| 23 | # Look for user with same emails | 23 | # Look for user with same emails |
| 24 | # | 24 | # |
| 25 | # Possible cases: | 25 | # Possible cases: |
| 26 | - # * When user already has account and need to link his LDAP account. | ||
| 27 | - # * LDAP uid changed for user with same email and we need to update his uid | 26 | + # * When user already has account and need to link their LDAP account. |
| 27 | + # * LDAP uid changed for user with same email and we need to update their uid | ||
| 28 | # | 28 | # |
| 29 | user = find_user(email) | 29 | user = find_user(email) |
| 30 | 30 | ||
| @@ -47,7 +47,7 @@ module Gitlab | @@ -47,7 +47,7 @@ module Gitlab | ||
| 47 | user = model.find_by_email(email) | 47 | user = model.find_by_email(email) |
| 48 | 48 | ||
| 49 | # If no user found and allow_username_or_email_login is true | 49 | # If no user found and allow_username_or_email_login is true |
| 50 | - # we look for user by extracting part of his email | 50 | + # we look for user by extracting part of their email |
| 51 | if !user && email && ldap_conf['allow_username_or_email_login'] | 51 | if !user && email && ldap_conf['allow_username_or_email_login'] |
| 52 | uname = email.partition('@').first | 52 | uname = email.partition('@').first |
| 53 | user = model.find_by_username(uname) | 53 | user = model.find_by_username(uname) |
lib/gitlab/regex.rb
| @@ -0,0 +1,50 @@ | @@ -0,0 +1,50 @@ | ||
| 1 | +require_relative 'file_action' | ||
| 2 | + | ||
| 3 | +module Gitlab | ||
| 4 | + module Satellite | ||
| 5 | + class DeleteFileAction < FileAction | ||
| 6 | + # Deletes file and creates a new commit for it | ||
| 7 | + # | ||
| 8 | + # Returns false if committing the change fails | ||
| 9 | + # Returns false if pushing from the satellite to bare repo failed or was rejected | ||
| 10 | + # Returns true otherwise | ||
| 11 | + def commit!(content, commit_message) | ||
| 12 | + in_locked_and_timed_satellite do |repo| | ||
| 13 | + prepare_satellite!(repo) | ||
| 14 | + | ||
| 15 | + # create target branch in satellite at the corresponding commit from bare repo | ||
| 16 | + repo.git.checkout({raise: true, timeout: true, b: true}, ref, "origin/#{ref}") | ||
| 17 | + | ||
| 18 | + # update the file in the satellite's working dir | ||
| 19 | + file_path_in_satellite = File.join(repo.working_dir, file_path) | ||
| 20 | + | ||
| 21 | + # Prevent relative links | ||
| 22 | + unless safe_path?(file_path_in_satellite) | ||
| 23 | + Gitlab::GitLogger.error("FileAction: Relative path not allowed") | ||
| 24 | + return false | ||
| 25 | + end | ||
| 26 | + | ||
| 27 | + File.delete(file_path_in_satellite) | ||
| 28 | + | ||
| 29 | + # add removed file | ||
| 30 | + repo.remove(file_path_in_satellite) | ||
| 31 | + | ||
| 32 | + # commit the changes | ||
| 33 | + # will raise CommandFailed when commit fails | ||
| 34 | + repo.git.commit(raise: true, timeout: true, a: true, m: commit_message) | ||
| 35 | + | ||
| 36 | + | ||
| 37 | + # push commit back to bare repo | ||
| 38 | + # will raise CommandFailed when push fails | ||
| 39 | + repo.git.push({raise: true, timeout: true}, :origin, ref) | ||
| 40 | + | ||
| 41 | + # everything worked | ||
| 42 | + true | ||
| 43 | + end | ||
| 44 | + rescue Grit::Git::CommandFailed => ex | ||
| 45 | + Gitlab::GitLogger.error(ex.message) | ||
| 46 | + false | ||
| 47 | + end | ||
| 48 | + end | ||
| 49 | + end | ||
| 50 | +end |
lib/gitlab/satellite/files/edit_file_action.rb
| @@ -8,19 +8,24 @@ module Gitlab | @@ -8,19 +8,24 @@ module Gitlab | ||
| 8 | # | 8 | # |
| 9 | # Returns false if the ref has been updated while editing the file | 9 | # Returns false if the ref has been updated while editing the file |
| 10 | # Returns false if committing the change fails | 10 | # Returns false if committing the change fails |
| 11 | - # Returns false if pushing from the satellite to Gitolite failed or was rejected | 11 | + # Returns false if pushing from the satellite to bare repo failed or was rejected |
| 12 | # Returns true otherwise | 12 | # Returns true otherwise |
| 13 | - def commit!(content, commit_message, last_commit) | ||
| 14 | - return false unless can_edit?(last_commit) | ||
| 15 | - | 13 | + def commit!(content, commit_message) |
| 16 | in_locked_and_timed_satellite do |repo| | 14 | in_locked_and_timed_satellite do |repo| |
| 17 | prepare_satellite!(repo) | 15 | prepare_satellite!(repo) |
| 18 | 16 | ||
| 19 | - # create target branch in satellite at the corresponding commit from Gitolite | 17 | + # create target branch in satellite at the corresponding commit from bare repo |
| 20 | repo.git.checkout({raise: true, timeout: true, b: true}, ref, "origin/#{ref}") | 18 | repo.git.checkout({raise: true, timeout: true, b: true}, ref, "origin/#{ref}") |
| 21 | 19 | ||
| 22 | # update the file in the satellite's working dir | 20 | # update the file in the satellite's working dir |
| 23 | file_path_in_satellite = File.join(repo.working_dir, file_path) | 21 | file_path_in_satellite = File.join(repo.working_dir, file_path) |
| 22 | + | ||
| 23 | + # Prevent relative links | ||
| 24 | + unless safe_path?(file_path_in_satellite) | ||
| 25 | + Gitlab::GitLogger.error("FileAction: Relative path not allowed") | ||
| 26 | + return false | ||
| 27 | + end | ||
| 28 | + | ||
| 24 | File.open(file_path_in_satellite, 'w') { |f| f.write(content) } | 29 | File.open(file_path_in_satellite, 'w') { |f| f.write(content) } |
| 25 | 30 | ||
| 26 | # commit the changes | 31 | # commit the changes |
| @@ -28,7 +33,7 @@ module Gitlab | @@ -28,7 +33,7 @@ module Gitlab | ||
| 28 | repo.git.commit(raise: true, timeout: true, a: true, m: commit_message) | 33 | repo.git.commit(raise: true, timeout: true, a: true, m: commit_message) |
| 29 | 34 | ||
| 30 | 35 | ||
| 31 | - # push commit back to Gitolite | 36 | + # push commit back to bare repo |
| 32 | # will raise CommandFailed when push fails | 37 | # will raise CommandFailed when push fails |
| 33 | repo.git.push({raise: true, timeout: true}, :origin, ref) | 38 | repo.git.push({raise: true, timeout: true}, :origin, ref) |
| 34 | 39 |
lib/gitlab/satellite/files/file_action.rb
| @@ -9,11 +9,8 @@ module Gitlab | @@ -9,11 +9,8 @@ module Gitlab | ||
| 9 | @ref = ref | 9 | @ref = ref |
| 10 | end | 10 | end |
| 11 | 11 | ||
| 12 | - protected | ||
| 13 | - | ||
| 14 | - def can_edit?(last_commit) | ||
| 15 | - current_last_commit = Gitlab::Git::Commit.last_for_path(@project.repository, ref, file_path).sha | ||
| 16 | - last_commit == current_last_commit | 12 | + def safe_path?(path) |
| 13 | + File.absolute_path(path) == path | ||
| 17 | end | 14 | end |
| 18 | end | 15 | end |
| 19 | end | 16 | end |
lib/gitlab/satellite/files/new_file_action.rb
| @@ -7,17 +7,28 @@ module Gitlab | @@ -7,17 +7,28 @@ module Gitlab | ||
| 7 | # | 7 | # |
| 8 | # Returns false if the ref has been updated while editing the file | 8 | # Returns false if the ref has been updated while editing the file |
| 9 | # Returns false if committing the change fails | 9 | # Returns false if committing the change fails |
| 10 | - # Returns false if pushing from the satellite to Gitolite failed or was rejected | 10 | + # Returns false if pushing from the satellite to bare repo failed or was rejected |
| 11 | # Returns true otherwise | 11 | # Returns true otherwise |
| 12 | - def commit!(content, commit_message, file_name) | 12 | + def commit!(content, commit_message) |
| 13 | in_locked_and_timed_satellite do |repo| | 13 | in_locked_and_timed_satellite do |repo| |
| 14 | prepare_satellite!(repo) | 14 | prepare_satellite!(repo) |
| 15 | 15 | ||
| 16 | - # create target branch in satellite at the corresponding commit from Gitolite | 16 | + # create target branch in satellite at the corresponding commit from bare repo |
| 17 | repo.git.checkout({raise: true, timeout: true, b: true}, ref, "origin/#{ref}") | 17 | repo.git.checkout({raise: true, timeout: true, b: true}, ref, "origin/#{ref}") |
| 18 | 18 | ||
| 19 | - # update the file in the satellite's working dir | ||
| 20 | - file_path_in_satellite = File.join(repo.working_dir, file_path, file_name) | 19 | + file_path_in_satellite = File.join(repo.working_dir, file_path) |
| 20 | + dir_name_in_satellite = File.dirname(file_path_in_satellite) | ||
| 21 | + | ||
| 22 | + # Prevent relative links | ||
| 23 | + unless safe_path?(file_path_in_satellite) | ||
| 24 | + Gitlab::GitLogger.error("FileAction: Relative path not allowed") | ||
| 25 | + return false | ||
| 26 | + end | ||
| 27 | + | ||
| 28 | + # Create dir if not exists | ||
| 29 | + FileUtils.mkdir_p(dir_name_in_satellite) | ||
| 30 | + | ||
| 31 | + # Write file | ||
| 21 | File.open(file_path_in_satellite, 'w') { |f| f.write(content) } | 32 | File.open(file_path_in_satellite, 'w') { |f| f.write(content) } |
| 22 | 33 | ||
| 23 | # add new file | 34 | # add new file |
| @@ -28,7 +39,7 @@ module Gitlab | @@ -28,7 +39,7 @@ module Gitlab | ||
| 28 | repo.git.commit(raise: true, timeout: true, a: true, m: commit_message) | 39 | repo.git.commit(raise: true, timeout: true, a: true, m: commit_message) |
| 29 | 40 | ||
| 30 | 41 | ||
| 31 | - # push commit back to Gitolite | 42 | + # push commit back to bare repo |
| 32 | # will raise CommandFailed when push fails | 43 | # will raise CommandFailed when push fails |
| 33 | repo.git.push({raise: true, timeout: true}, :origin, ref) | 44 | repo.git.push({raise: true, timeout: true}, :origin, ref) |
| 34 | 45 |
lib/gitlab/satellite/merge_action.rb
| @@ -28,7 +28,7 @@ module Gitlab | @@ -28,7 +28,7 @@ module Gitlab | ||
| 28 | in_locked_and_timed_satellite do |merge_repo| | 28 | in_locked_and_timed_satellite do |merge_repo| |
| 29 | prepare_satellite!(merge_repo) | 29 | prepare_satellite!(merge_repo) |
| 30 | if merge_in_satellite!(merge_repo) | 30 | if merge_in_satellite!(merge_repo) |
| 31 | - # push merge back to Gitolite | 31 | + # push merge back to bare repo |
| 32 | # will raise CommandFailed when push fails | 32 | # will raise CommandFailed when push fails |
| 33 | merge_repo.git.push(default_options, :origin, merge_request.target_branch) | 33 | merge_repo.git.push(default_options, :origin, merge_request.target_branch) |
| 34 | # remove source branch | 34 | # remove source branch |
lib/gitlab/satellite/satellite.rb
| @@ -123,7 +123,7 @@ module Gitlab | @@ -123,7 +123,7 @@ module Gitlab | ||
| 123 | remotes.each { |name| repo.git.remote(default_options,'rm', name)} | 123 | remotes.each { |name| repo.git.remote(default_options,'rm', name)} |
| 124 | end | 124 | end |
| 125 | 125 | ||
| 126 | - # Updates the satellite from Gitolite | 126 | + # Updates the satellite from bare repo |
| 127 | # | 127 | # |
| 128 | # Note: this will only update remote branches (i.e. origin/*) | 128 | # Note: this will only update remote branches (i.e. origin/*) |
| 129 | def update_from_source! | 129 | def update_from_source! |
| @@ -0,0 +1,42 @@ | @@ -0,0 +1,42 @@ | ||
| 1 | +# Gitlab::VisibilityLevel module | ||
| 2 | +# | ||
| 3 | +# Define allowed public modes that can be used for | ||
| 4 | +# GitLab projects to determine project public mode | ||
| 5 | +# | ||
| 6 | +module Gitlab | ||
| 7 | + module VisibilityLevel | ||
| 8 | + PRIVATE = 0 | ||
| 9 | + INTERNAL = 10 | ||
| 10 | + PUBLIC = 20 | ||
| 11 | + | ||
| 12 | + class << self | ||
| 13 | + def values | ||
| 14 | + options.values | ||
| 15 | + end | ||
| 16 | + | ||
| 17 | + def options | ||
| 18 | + { | ||
| 19 | + 'Private' => PRIVATE, | ||
| 20 | + 'Internal' => INTERNAL, | ||
| 21 | + 'Public' => PUBLIC | ||
| 22 | + } | ||
| 23 | + end | ||
| 24 | + | ||
| 25 | + def allowed_for?(user, level) | ||
| 26 | + user.is_admin? || !Gitlab.config.gitlab.restricted_visibility_levels.include?(level) | ||
| 27 | + end | ||
| 28 | + end | ||
| 29 | + | ||
| 30 | + def private? | ||
| 31 | + visibility_level_field == PRIVATE | ||
| 32 | + end | ||
| 33 | + | ||
| 34 | + def internal? | ||
| 35 | + visibility_level_field == INTERNAL | ||
| 36 | + end | ||
| 37 | + | ||
| 38 | + def public? | ||
| 39 | + visibility_level_field == PUBLIC | ||
| 40 | + end | ||
| 41 | + end | ||
| 42 | +end |
lib/redcarpet/render/gitlab_html.rb
| @@ -36,7 +36,7 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML | @@ -36,7 +36,7 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML | ||
| 36 | 36 | ||
| 37 | def preprocess(full_document) | 37 | def preprocess(full_document) |
| 38 | if @project | 38 | if @project |
| 39 | - h.create_relative_links(full_document, @project.path_with_namespace, @ref, @request_path, is_wiki?) | 39 | + h.create_relative_links(full_document, @project, @ref, @request_path, is_wiki?) |
| 40 | else | 40 | else |
| 41 | full_document | 41 | full_document |
| 42 | end | 42 | end |
lib/support/init.d/gitlab
| @@ -31,6 +31,8 @@ sidekiq_pid_path="$pid_path/sidekiq.pid" | @@ -31,6 +31,8 @@ sidekiq_pid_path="$pid_path/sidekiq.pid" | ||
| 31 | 31 | ||
| 32 | ### Here ends user configuration ### | 32 | ### Here ends user configuration ### |
| 33 | 33 | ||
| 34 | +# Read configuration variable file if it is present | ||
| 35 | +test -f /etc/default/gitlab && . /etc/default/gitlab | ||
| 34 | 36 | ||
| 35 | # Switch to the app_user if it is not he/she who is running the script. | 37 | # Switch to the app_user if it is not he/she who is running the script. |
| 36 | if [ "$USER" != "$app_user" ]; then | 38 | if [ "$USER" != "$app_user" ]; then |
lib/support/nginx/gitlab
| @@ -11,6 +11,9 @@ server { | @@ -11,6 +11,9 @@ server { | ||
| 11 | server_name YOUR_SERVER_FQDN; # e.g., server_name source.example.com; | 11 | server_name YOUR_SERVER_FQDN; # e.g., server_name source.example.com; |
| 12 | server_tokens off; # don't show the version number, a security best practice | 12 | server_tokens off; # don't show the version number, a security best practice |
| 13 | root /home/git/gitlab/public; | 13 | root /home/git/gitlab/public; |
| 14 | + | ||
| 15 | + # Set value of client_max_body_size to at least the value of git.max_size in gitlab.yml | ||
| 16 | + client_max_body_size 5m; | ||
| 14 | 17 | ||
| 15 | # individual nginx logs for this gitlab vhost | 18 | # individual nginx logs for this gitlab vhost |
| 16 | access_log /var/log/nginx/gitlab_access.log; | 19 | access_log /var/log/nginx/gitlab_access.log; |
lib/tasks/gitlab/check.rake
| @@ -3,6 +3,7 @@ namespace :gitlab do | @@ -3,6 +3,7 @@ namespace :gitlab do | ||
| 3 | task check: %w{gitlab:env:check | 3 | task check: %w{gitlab:env:check |
| 4 | gitlab:gitlab_shell:check | 4 | gitlab:gitlab_shell:check |
| 5 | gitlab:sidekiq:check | 5 | gitlab:sidekiq:check |
| 6 | + gitlab:ldap:check | ||
| 6 | gitlab:app:check} | 7 | gitlab:app:check} |
| 7 | 8 | ||
| 8 | 9 | ||
| @@ -611,10 +612,7 @@ namespace :gitlab do | @@ -611,10 +612,7 @@ namespace :gitlab do | ||
| 611 | end | 612 | end |
| 612 | 613 | ||
| 613 | def gitlab_shell_version | 614 | def gitlab_shell_version |
| 614 | - gitlab_shell_version_file = "#{gitlab_shell_user_home}/gitlab-shell/VERSION" | ||
| 615 | - if File.readable?(gitlab_shell_version_file) | ||
| 616 | - File.read(gitlab_shell_version_file) | ||
| 617 | - end | 615 | + Gitlab::Shell.new.version |
| 618 | end | 616 | end |
| 619 | 617 | ||
| 620 | def has_gitlab_shell3? | 618 | def has_gitlab_shell3? |
| @@ -682,6 +680,44 @@ namespace :gitlab do | @@ -682,6 +680,44 @@ namespace :gitlab do | ||
| 682 | end | 680 | end |
| 683 | end | 681 | end |
| 684 | 682 | ||
| 683 | + namespace :ldap do | ||
| 684 | + task :check, [:limit] => :environment do |t, args| | ||
| 685 | + args.with_defaults(limit: 100) | ||
| 686 | + warn_user_is_not_gitlab | ||
| 687 | + start_checking "LDAP" | ||
| 688 | + | ||
| 689 | + if ldap_config.enabled | ||
| 690 | + print_users(args.limit) | ||
| 691 | + else | ||
| 692 | + puts 'LDAP is disabled in config/gitlab.yml' | ||
| 693 | + end | ||
| 694 | + | ||
| 695 | + finished_checking "LDAP" | ||
| 696 | + end | ||
| 697 | + | ||
| 698 | + def print_users(limit) | ||
| 699 | + puts "LDAP users with access to your GitLab server (limit: #{limit}):" | ||
| 700 | + ldap.search(attributes: attributes, filter: filter, size: limit, return_result: false) do |entry| | ||
| 701 | + puts "DN: #{entry.dn}\t#{ldap_config.uid}: #{entry[ldap_config.uid]}" | ||
| 702 | + end | ||
| 703 | + end | ||
| 704 | + | ||
| 705 | + def attributes | ||
| 706 | + [ldap_config.uid] | ||
| 707 | + end | ||
| 708 | + | ||
| 709 | + def filter | ||
| 710 | + Net::LDAP::Filter.present?(ldap_config.uid) | ||
| 711 | + end | ||
| 712 | + | ||
| 713 | + def ldap | ||
| 714 | + @ldap ||= OmniAuth::LDAP::Adaptor.new(ldap_config).connection | ||
| 715 | + end | ||
| 716 | + | ||
| 717 | + def ldap_config | ||
| 718 | + @ldap_config ||= Gitlab.config.ldap | ||
| 719 | + end | ||
| 720 | + end | ||
| 685 | 721 | ||
| 686 | # Helper methods | 722 | # Helper methods |
| 687 | ########################## | 723 | ########################## |
| @@ -736,7 +772,7 @@ namespace :gitlab do | @@ -736,7 +772,7 @@ namespace :gitlab do | ||
| 736 | end | 772 | end |
| 737 | 773 | ||
| 738 | def check_gitlab_shell | 774 | def check_gitlab_shell |
| 739 | - required_version = Gitlab::VersionInfo.new(1, 7, 4) | 775 | + required_version = Gitlab::VersionInfo.new(1, 7, 9) |
| 740 | current_version = Gitlab::VersionInfo.parse(gitlab_shell_version) | 776 | current_version = Gitlab::VersionInfo.parse(gitlab_shell_version) |
| 741 | 777 | ||
| 742 | print "GitLab Shell version >= #{required_version} ? ... " | 778 | print "GitLab Shell version >= #{required_version} ? ... " |
lib/tasks/gitlab/task_helpers.rake
| @@ -2,6 +2,16 @@ module Gitlab | @@ -2,6 +2,16 @@ module Gitlab | ||
| 2 | class TaskAbortedByUserError < StandardError; end | 2 | class TaskAbortedByUserError < StandardError; end |
| 3 | end | 3 | end |
| 4 | 4 | ||
| 5 | +unless STDOUT.isatty | ||
| 6 | + module Colored | ||
| 7 | + extend self | ||
| 8 | + | ||
| 9 | + def colorize(string, options={}) | ||
| 10 | + string | ||
| 11 | + end | ||
| 12 | + end | ||
| 13 | +end | ||
| 14 | + | ||
| 5 | namespace :gitlab do | 15 | namespace :gitlab do |
| 6 | 16 | ||
| 7 | # Ask if the user wants to continue | 17 | # Ask if the user wants to continue |
public/favicon.ico
No preview for this file type
| @@ -0,0 +1,73 @@ | @@ -0,0 +1,73 @@ | ||
| 1 | +require 'spec_helper' | ||
| 2 | + | ||
| 3 | +describe Issues::ListContext do | ||
| 4 | + | ||
| 5 | + let(:user) { create(:user) } | ||
| 6 | + let(:project) { create(:project, creator: user) } | ||
| 7 | + | ||
| 8 | + titles = ['foo','bar','baz'] | ||
| 9 | + titles.each_with_index do |title, index| | ||
| 10 | + let!(title.to_sym) { create(:issue, title: title, project: project, created_at: Time.now - (index * 60)) } | ||
| 11 | + end | ||
| 12 | + | ||
| 13 | + describe 'sorting' do | ||
| 14 | + it 'sorts by newest' do | ||
| 15 | + params = {sort: 'newest'} | ||
| 16 | + | ||
| 17 | + issues = Issues::ListContext.new(project, user, params).execute | ||
| 18 | + issues.first.should eq foo | ||
| 19 | + end | ||
| 20 | + | ||
| 21 | + it 'sorts by oldest' do | ||
| 22 | + params = {sort: 'oldest'} | ||
| 23 | + | ||
| 24 | + issues = Issues::ListContext.new(project, user, params).execute | ||
| 25 | + issues.first.should eq baz | ||
| 26 | + end | ||
| 27 | + | ||
| 28 | + it 'sorts by recently updated' do | ||
| 29 | + params = {sort: 'recently_updated'} | ||
| 30 | + baz.updated_at = Time.now + 10 | ||
| 31 | + baz.save | ||
| 32 | + | ||
| 33 | + issues = Issues::ListContext.new(project, user, params).execute | ||
| 34 | + issues.first.should eq baz | ||
| 35 | + end | ||
| 36 | + | ||
| 37 | + it 'sorts by least recently updated' do | ||
| 38 | + params = {sort: 'last_updated'} | ||
| 39 | + bar.updated_at = Time.now - 10 | ||
| 40 | + bar.save | ||
| 41 | + | ||
| 42 | + issues = Issues::ListContext.new(project, user, params).execute | ||
| 43 | + issues.first.should eq bar | ||
| 44 | + end | ||
| 45 | + | ||
| 46 | + describe 'sorting by milestone' do | ||
| 47 | + let(:newer_due_milestone) { create(:milestone, due_date: '2013-12-11') } | ||
| 48 | + let(:later_due_milestone) { create(:milestone, due_date: '2013-12-12') } | ||
| 49 | + | ||
| 50 | + before :each do | ||
| 51 | + foo.milestone = newer_due_milestone | ||
| 52 | + foo.save | ||
| 53 | + bar.milestone = later_due_milestone | ||
| 54 | + bar.save | ||
| 55 | + end | ||
| 56 | + | ||
| 57 | + it 'sorts by most recently due milestone' do | ||
| 58 | + params = {sort: 'milestone_due_soon'} | ||
| 59 | + | ||
| 60 | + issues = Issues::ListContext.new(project, user, params).execute | ||
| 61 | + issues.first.should eq foo | ||
| 62 | + | ||
| 63 | + end | ||
| 64 | + | ||
| 65 | + it 'sorts by least recently due milestone' do | ||
| 66 | + params = {sort: 'milestone_due_later'} | ||
| 67 | + | ||
| 68 | + issues = Issues::ListContext.new(project, user, params).execute | ||
| 69 | + issues.first.should eq bar | ||
| 70 | + end | ||
| 71 | + end | ||
| 72 | + end | ||
| 73 | +end |
spec/contexts/projects_create_context_spec.rb
| @@ -7,6 +7,7 @@ describe Projects::CreateContext do | @@ -7,6 +7,7 @@ describe Projects::CreateContext do | ||
| 7 | describe :create_by_user do | 7 | describe :create_by_user do |
| 8 | before do | 8 | before do |
| 9 | @user = create :user | 9 | @user = create :user |
| 10 | + @admin = create :user, admin: true | ||
| 10 | @opts = { | 11 | @opts = { |
| 11 | name: "GitLab", | 12 | name: "GitLab", |
| 12 | namespace: @user.namespace | 13 | namespace: @user.namespace |
| @@ -37,7 +38,7 @@ describe Projects::CreateContext do | @@ -37,7 +38,7 @@ describe Projects::CreateContext do | ||
| 37 | it { @project.namespace.should == @group } | 38 | it { @project.namespace.should == @group } |
| 38 | end | 39 | end |
| 39 | 40 | ||
| 40 | - context 'respect configured public setting' do | 41 | + context 'respect configured visibility setting' do |
| 41 | before(:each) do | 42 | before(:each) do |
| 42 | @settings = double("settings") | 43 | @settings = double("settings") |
| 43 | @settings.stub(:issues) { true } | 44 | @settings.stub(:issues) { true } |
| @@ -46,25 +47,90 @@ describe Projects::CreateContext do | @@ -46,25 +47,90 @@ describe Projects::CreateContext do | ||
| 46 | @settings.stub(:wall) { true } | 47 | @settings.stub(:wall) { true } |
| 47 | @settings.stub(:snippets) { true } | 48 | @settings.stub(:snippets) { true } |
| 48 | stub_const("Settings", Class.new) | 49 | stub_const("Settings", Class.new) |
| 50 | + @restrictions = double("restrictions") | ||
| 51 | + @restrictions.stub(:restricted_visibility_levels) { [] } | ||
| 52 | + Settings.stub_chain(:gitlab).and_return(@restrictions) | ||
| 49 | Settings.stub_chain(:gitlab, :default_projects_features).and_return(@settings) | 53 | Settings.stub_chain(:gitlab, :default_projects_features).and_return(@settings) |
| 50 | end | 54 | end |
| 51 | 55 | ||
| 52 | context 'should be public when setting is public' do | 56 | context 'should be public when setting is public' do |
| 53 | before do | 57 | before do |
| 54 | - @settings.stub(:public) { true } | 58 | + @settings.stub(:visibility_level) { Gitlab::VisibilityLevel::PUBLIC } |
| 55 | @project = create_project(@user, @opts) | 59 | @project = create_project(@user, @opts) |
| 56 | end | 60 | end |
| 57 | 61 | ||
| 58 | - it { @project.public.should be_true } | 62 | + it { @project.public?.should be_true } |
| 59 | end | 63 | end |
| 60 | 64 | ||
| 61 | - context 'should be private when setting is not public' do | 65 | + context 'should be private when setting is private' do |
| 62 | before do | 66 | before do |
| 63 | - @settings.stub(:public) { false } | 67 | + @settings.stub(:visibility_level) { Gitlab::VisibilityLevel::PRIVATE } |
| 64 | @project = create_project(@user, @opts) | 68 | @project = create_project(@user, @opts) |
| 65 | end | 69 | end |
| 66 | 70 | ||
| 67 | - it { @project.public.should be_false } | 71 | + it { @project.private?.should be_true } |
| 72 | + end | ||
| 73 | + | ||
| 74 | + context 'should be internal when setting is internal' do | ||
| 75 | + before do | ||
| 76 | + @settings.stub(:visibility_level) { Gitlab::VisibilityLevel::INTERNAL } | ||
| 77 | + @project = create_project(@user, @opts) | ||
| 78 | + end | ||
| 79 | + | ||
| 80 | + it { @project.internal?.should be_true } | ||
| 81 | + end | ||
| 82 | + end | ||
| 83 | + | ||
| 84 | + context 'respect configured visibility restrictions setting' do | ||
| 85 | + before(:each) do | ||
| 86 | + @settings = double("settings") | ||
| 87 | + @settings.stub(:issues) { true } | ||
| 88 | + @settings.stub(:merge_requests) { true } | ||
| 89 | + @settings.stub(:wiki) { true } | ||
| 90 | + @settings.stub(:wall) { true } | ||
| 91 | + @settings.stub(:snippets) { true } | ||
| 92 | + @settings.stub(:visibility_level) { Gitlab::VisibilityLevel::PRIVATE } | ||
| 93 | + stub_const("Settings", Class.new) | ||
| 94 | + @restrictions = double("restrictions") | ||
| 95 | + @restrictions.stub(:restricted_visibility_levels) { [ Gitlab::VisibilityLevel::PUBLIC ] } | ||
| 96 | + Settings.stub_chain(:gitlab).and_return(@restrictions) | ||
| 97 | + Settings.stub_chain(:gitlab, :default_projects_features).and_return(@settings) | ||
| 98 | + end | ||
| 99 | + | ||
| 100 | + context 'should be private when option is public' do | ||
| 101 | + before do | ||
| 102 | + @opts.merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC) | ||
| 103 | + @project = create_project(@user, @opts) | ||
| 104 | + end | ||
| 105 | + | ||
| 106 | + it { @project.private?.should be_true } | ||
| 107 | + end | ||
| 108 | + | ||
| 109 | + context 'should be public when option is public for admin' do | ||
| 110 | + before do | ||
| 111 | + @opts.merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC) | ||
| 112 | + @project = create_project(@admin, @opts) | ||
| 113 | + end | ||
| 114 | + | ||
| 115 | + it { @project.public?.should be_true } | ||
| 116 | + end | ||
| 117 | + | ||
| 118 | + context 'should be private when option is private' do | ||
| 119 | + before do | ||
| 120 | + @opts.merge!(visibility_level: Gitlab::VisibilityLevel::PRIVATE) | ||
| 121 | + @project = create_project(@user, @opts) | ||
| 122 | + end | ||
| 123 | + | ||
| 124 | + it { @project.private?.should be_true } | ||
| 125 | + end | ||
| 126 | + | ||
| 127 | + context 'should be internal when option is internal' do | ||
| 128 | + before do | ||
| 129 | + @opts.merge!(visibility_level: Gitlab::VisibilityLevel::INTERNAL) | ||
| 130 | + @project = create_project(@user, @opts) | ||
| 131 | + end | ||
| 132 | + | ||
| 133 | + it { @project.internal?.should be_true } | ||
| 68 | end | 134 | end |
| 69 | end | 135 | end |
| 70 | end | 136 | end |
| @@ -73,3 +139,4 @@ describe Projects::CreateContext do | @@ -73,3 +139,4 @@ describe Projects::CreateContext do | ||
| 73 | Projects::CreateContext.new(user, opts).execute | 139 | Projects::CreateContext.new(user, opts).execute |
| 74 | end | 140 | end |
| 75 | end | 141 | end |
| 142 | + |
| @@ -0,0 +1,111 @@ | @@ -0,0 +1,111 @@ | ||
| 1 | +require 'spec_helper' | ||
| 2 | + | ||
| 3 | +describe Projects::UpdateContext do | ||
| 4 | + before(:each) { ActiveRecord::Base.observers.enable(:user_observer) } | ||
| 5 | + after(:each) { ActiveRecord::Base.observers.disable(:user_observer) } | ||
| 6 | + | ||
| 7 | + describe :update_by_user do | ||
| 8 | + before do | ||
| 9 | + @user = create :user | ||
| 10 | + @admin = create :user, admin: true | ||
| 11 | + @project = create :project, creator_id: @user.id, namespace: @user.namespace | ||
| 12 | + @opts = { project: {} } | ||
| 13 | + end | ||
| 14 | + | ||
| 15 | + context 'should be private when updated to private' do | ||
| 16 | + before do | ||
| 17 | + @created_private = @project.private? | ||
| 18 | + | ||
| 19 | + @opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::PRIVATE) | ||
| 20 | + update_project(@project, @user, @opts) | ||
| 21 | + end | ||
| 22 | + | ||
| 23 | + it { @created_private.should be_true } | ||
| 24 | + it { @project.private?.should be_true } | ||
| 25 | + end | ||
| 26 | + | ||
| 27 | + context 'should be internal when updated to internal' do | ||
| 28 | + before do | ||
| 29 | + @created_private = @project.private? | ||
| 30 | + | ||
| 31 | + @opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::INTERNAL) | ||
| 32 | + update_project(@project, @user, @opts) | ||
| 33 | + end | ||
| 34 | + | ||
| 35 | + it { @created_private.should be_true } | ||
| 36 | + it { @project.internal?.should be_true } | ||
| 37 | + end | ||
| 38 | + | ||
| 39 | + context 'should be public when updated to public' do | ||
| 40 | + before do | ||
| 41 | + @created_private = @project.private? | ||
| 42 | + | ||
| 43 | + @opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC) | ||
| 44 | + update_project(@project, @user, @opts) | ||
| 45 | + end | ||
| 46 | + | ||
| 47 | + it { @created_private.should be_true } | ||
| 48 | + it { @project.public?.should be_true } | ||
| 49 | + end | ||
| 50 | + | ||
| 51 | + context 'respect configured visibility restrictions setting' do | ||
| 52 | + before(:each) do | ||
| 53 | + @restrictions = double("restrictions") | ||
| 54 | + @restrictions.stub(:restricted_visibility_levels) { [ Gitlab::VisibilityLevel::PUBLIC ] } | ||
| 55 | + Settings.stub_chain(:gitlab).and_return(@restrictions) | ||
| 56 | + end | ||
| 57 | + | ||
| 58 | + context 'should be private when updated to private' do | ||
| 59 | + before do | ||
| 60 | + @created_private = @project.private? | ||
| 61 | + | ||
| 62 | + @opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::PRIVATE) | ||
| 63 | + update_project(@project, @user, @opts) | ||
| 64 | + end | ||
| 65 | + | ||
| 66 | + it { @created_private.should be_true } | ||
| 67 | + it { @project.private?.should be_true } | ||
| 68 | + end | ||
| 69 | + | ||
| 70 | + context 'should be internal when updated to internal' do | ||
| 71 | + before do | ||
| 72 | + @created_private = @project.private? | ||
| 73 | + | ||
| 74 | + @opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::INTERNAL) | ||
| 75 | + update_project(@project, @user, @opts) | ||
| 76 | + end | ||
| 77 | + | ||
| 78 | + it { @created_private.should be_true } | ||
| 79 | + it { @project.internal?.should be_true } | ||
| 80 | + end | ||
| 81 | + | ||
| 82 | + context 'should be private when updated to public' do | ||
| 83 | + before do | ||
| 84 | + @created_private = @project.private? | ||
| 85 | + | ||
| 86 | + @opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC) | ||
| 87 | + update_project(@project, @user, @opts) | ||
| 88 | + end | ||
| 89 | + | ||
| 90 | + it { @created_private.should be_true } | ||
| 91 | + it { @project.private?.should be_true } | ||
| 92 | + end | ||
| 93 | + | ||
| 94 | + context 'should be public when updated to public by admin' do | ||
| 95 | + before do | ||
| 96 | + @created_private = @project.private? | ||
| 97 | + | ||
| 98 | + @opts[:project].merge!(visibility_level: Gitlab::VisibilityLevel::PUBLIC) | ||
| 99 | + update_project(@project, @admin, @opts) | ||
| 100 | + end | ||
| 101 | + | ||
| 102 | + it { @created_private.should be_true } | ||
| 103 | + it { @project.public?.should be_true } | ||
| 104 | + end | ||
| 105 | + end | ||
| 106 | + end | ||
| 107 | + | ||
| 108 | + def update_project(project, user, opts) | ||
| 109 | + Projects::UpdateContext.new(project, user, opts).execute | ||
| 110 | + end | ||
| 111 | +end | ||
| 0 | \ No newline at end of file | 112 | \ No newline at end of file |
spec/contexts/search_context_spec.rb
| @@ -3,23 +3,39 @@ require 'spec_helper' | @@ -3,23 +3,39 @@ require 'spec_helper' | ||
| 3 | describe SearchContext do | 3 | describe SearchContext do |
| 4 | let(:found_namespace) { create(:namespace, name: 'searchable namespace', path:'another_thing') } | 4 | let(:found_namespace) { create(:namespace, name: 'searchable namespace', path:'another_thing') } |
| 5 | let(:user) { create(:user, namespace: found_namespace) } | 5 | let(:user) { create(:user, namespace: found_namespace) } |
| 6 | - let!(:found_project) { create(:project, name: 'searchable_project', creator_id: user.id, namespace: found_namespace, public: false) } | 6 | + let!(:found_project) { create(:project, name: 'searchable_project', creator_id: user.id, namespace: found_namespace, visibility_level: Gitlab::VisibilityLevel::PRIVATE) } |
| 7 | 7 | ||
| 8 | let(:unfound_namespace) { create(:namespace, name: 'unfound namespace', path: 'yet_something_else') } | 8 | let(:unfound_namespace) { create(:namespace, name: 'unfound namespace', path: 'yet_something_else') } |
| 9 | - let!(:unfound_project) { create(:project, name: 'unfound_project', creator_id: user.id, namespace: unfound_namespace, public: false) } | ||
| 10 | - let(:public_namespace) { create(:namespace, path: 'something_else',name: 'searchable public namespace') } | ||
| 11 | - let(:other_user) { create(:user, namespace: public_namespace) } | ||
| 12 | - let!(:public_project) { create(:project, name: 'searchable_public_project', creator_id: other_user.id, namespace: public_namespace, public: true) } | 9 | + let!(:unfound_project) { create(:project, name: 'unfound_project', creator_id: user.id, namespace: unfound_namespace, visibility_level: Gitlab::VisibilityLevel::PRIVATE) } |
| 10 | + | ||
| 11 | + let(:internal_namespace) { create(:namespace, path: 'something_internal',name: 'searchable internal namespace') } | ||
| 12 | + let(:internal_user) { create(:user, namespace: internal_namespace) } | ||
| 13 | + let!(:internal_project) { create(:project, name: 'searchable_internal_project', creator_id: internal_user.id, namespace: internal_namespace, visibility_level: Gitlab::VisibilityLevel::INTERNAL) } | ||
| 14 | + | ||
| 15 | + let(:public_namespace) { create(:namespace, path: 'something_public',name: 'searchable public namespace') } | ||
| 16 | + let(:public_user) { create(:user, namespace: public_namespace) } | ||
| 17 | + let!(:public_project) { create(:project, name: 'searchable_public_project', creator_id: public_user.id, namespace: public_namespace, visibility_level: Gitlab::VisibilityLevel::PUBLIC) } | ||
| 13 | 18 | ||
| 14 | describe '#execute' do | 19 | describe '#execute' do |
| 15 | it 'public projects should be searchable' do | 20 | it 'public projects should be searchable' do |
| 16 | - context = SearchContext.new([found_project.id], {search_code: false, search: "searchable"}) | 21 | + context = SearchContext.new([found_project.id], nil, {search_code: false, search: "searchable"}) |
| 17 | results = context.execute | 22 | results = context.execute |
| 18 | results[:projects].should == [found_project, public_project] | 23 | results[:projects].should == [found_project, public_project] |
| 19 | end | 24 | end |
| 20 | 25 | ||
| 26 | + it 'internal projects should be searchable' do | ||
| 27 | + context = SearchContext.new([found_project.id], user, {search_code: false, search: "searchable"}) | ||
| 28 | + results = context.execute | ||
| 29 | + # can't seem to rely on the return order, so check this way | ||
| 30 | + #subject { results[:projects] } | ||
| 31 | + results[:projects].should have(3).items | ||
| 32 | + results[:projects].should include(found_project) | ||
| 33 | + results[:projects].should include(internal_project) | ||
| 34 | + results[:projects].should include(public_project) | ||
| 35 | + end | ||
| 36 | + | ||
| 21 | it 'namespace name should be searchable' do | 37 | it 'namespace name should be searchable' do |
| 22 | - context = SearchContext.new([found_project.id], {search_code: false, search: "searchable namespace"}) | 38 | + context = SearchContext.new([found_project.id], user, {search_code: false, search: "searchable namespace"}) |
| 23 | results = context.execute | 39 | results = context.execute |
| 24 | results[:projects].should == [found_project] | 40 | results[:projects].should == [found_project] |
| 25 | end | 41 | end |
spec/factories.rb
| @@ -15,7 +15,7 @@ FactoryGirl.define do | @@ -15,7 +15,7 @@ FactoryGirl.define do | ||
| 15 | email { Faker::Internet.email } | 15 | email { Faker::Internet.email } |
| 16 | name | 16 | name |
| 17 | sequence(:username) { |n| "#{Faker::Internet.user_name}#{n}" } | 17 | sequence(:username) { |n| "#{Faker::Internet.user_name}#{n}" } |
| 18 | - password "123456" | 18 | + password "12345678" |
| 19 | password_confirmation { password } | 19 | password_confirmation { password } |
| 20 | confirmed_at { Time.now } | 20 | confirmed_at { Time.now } |
| 21 | confirmation_token { nil } | 21 | confirmation_token { nil } |
| @@ -66,6 +66,7 @@ FactoryGirl.define do | @@ -66,6 +66,7 @@ FactoryGirl.define do | ||
| 66 | 66 | ||
| 67 | after :create do |project| | 67 | after :create do |project| |
| 68 | TestEnv.clear_repo_dir(project.namespace, project.path) | 68 | TestEnv.clear_repo_dir(project.namespace, project.path) |
| 69 | + TestEnv.reset_satellite_dir | ||
| 69 | TestEnv.create_repo(project.namespace, project.path) | 70 | TestEnv.create_repo(project.namespace, project.path) |
| 70 | end | 71 | end |
| 71 | end | 72 | end |
| @@ -0,0 +1,23 @@ | @@ -0,0 +1,23 @@ | ||
| 1 | +# == Schema Information | ||
| 2 | +# | ||
| 3 | +# Table name: broadcast_messages | ||
| 4 | +# | ||
| 5 | +# id :integer not null, primary key | ||
| 6 | +# message :text default(""), not null | ||
| 7 | +# starts_at :datetime | ||
| 8 | +# ends_at :datetime | ||
| 9 | +# alert_type :integer | ||
| 10 | +# created_at :datetime not null | ||
| 11 | +# updated_at :datetime not null | ||
| 12 | +# | ||
| 13 | + | ||
| 14 | +# Read about factories at https://github.com/thoughtbot/factory_girl | ||
| 15 | + | ||
| 16 | +FactoryGirl.define do | ||
| 17 | + factory :broadcast_message do | ||
| 18 | + message "MyText" | ||
| 19 | + starts_at "2013-11-12 13:43:25" | ||
| 20 | + ends_at "2013-11-12 13:43:25" | ||
| 21 | + alert_type 1 | ||
| 22 | + end | ||
| 23 | +end |
spec/features/issues_spec.rb
| @@ -95,4 +95,91 @@ describe "Issues" do | @@ -95,4 +95,91 @@ describe "Issues" do | ||
| 95 | page.should have_content 'gitlab' | 95 | page.should have_content 'gitlab' |
| 96 | end | 96 | end |
| 97 | end | 97 | end |
| 98 | + | ||
| 99 | + describe 'filter issue' do | ||
| 100 | + titles = ['foo','bar','baz'] | ||
| 101 | + titles.each_with_index do |title, index| | ||
| 102 | + let!(title.to_sym) { create(:issue, title: title, project: project, created_at: Time.now - (index * 60)) } | ||
| 103 | + end | ||
| 104 | + let(:newer_due_milestone) { create(:milestone, due_date: '2013-12-11') } | ||
| 105 | + let(:later_due_milestone) { create(:milestone, due_date: '2013-12-12') } | ||
| 106 | + | ||
| 107 | + it 'sorts by newest' do | ||
| 108 | + visit project_issues_path(project, sort: 'newest') | ||
| 109 | + | ||
| 110 | + first_issue.should include("foo") | ||
| 111 | + last_issue.should include("baz") | ||
| 112 | + end | ||
| 113 | + | ||
| 114 | + it 'sorts by oldest' do | ||
| 115 | + visit project_issues_path(project, sort: 'oldest') | ||
| 116 | + | ||
| 117 | + first_issue.should include("baz") | ||
| 118 | + last_issue.should include("foo") | ||
| 119 | + end | ||
| 120 | + | ||
| 121 | + it 'sorts by most recently updated' do | ||
| 122 | + baz.updated_at = Time.now + 100 | ||
| 123 | + baz.save | ||
| 124 | + visit project_issues_path(project, sort: 'recently_updated') | ||
| 125 | + | ||
| 126 | + first_issue.should include("baz") | ||
| 127 | + end | ||
| 128 | + | ||
| 129 | + it 'sorts by least recently updated' do | ||
| 130 | + baz.updated_at = Time.now - 100 | ||
| 131 | + baz.save | ||
| 132 | + visit project_issues_path(project, sort: 'last_updated') | ||
| 133 | + | ||
| 134 | + first_issue.should include("baz") | ||
| 135 | + end | ||
| 136 | + | ||
| 137 | + describe 'sorting by milestone' do | ||
| 138 | + before :each do | ||
| 139 | + foo.milestone = newer_due_milestone | ||
| 140 | + foo.save | ||
| 141 | + bar.milestone = later_due_milestone | ||
| 142 | + bar.save | ||
| 143 | + end | ||
| 144 | + | ||
| 145 | + it 'sorts by recently due milestone' do | ||
| 146 | + visit project_issues_path(project, sort: 'milestone_due_soon') | ||
| 147 | + | ||
| 148 | + first_issue.should include("foo") | ||
| 149 | + end | ||
| 150 | + | ||
| 151 | + it 'sorts by least recently due milestone' do | ||
| 152 | + visit project_issues_path(project, sort: 'milestone_due_later') | ||
| 153 | + | ||
| 154 | + first_issue.should include("bar") | ||
| 155 | + end | ||
| 156 | + end | ||
| 157 | + | ||
| 158 | + describe 'combine filter and sort' do | ||
| 159 | + let(:user2) { create(:user) } | ||
| 160 | + | ||
| 161 | + before :each do | ||
| 162 | + foo.assignee = user2 | ||
| 163 | + foo.save | ||
| 164 | + bar.assignee = user2 | ||
| 165 | + bar.save | ||
| 166 | + end | ||
| 167 | + | ||
| 168 | + it 'sorts with a filter applied' do | ||
| 169 | + visit project_issues_path(project, sort: 'oldest', assignee_id: user2.id) | ||
| 170 | + | ||
| 171 | + first_issue.should include("bar") | ||
| 172 | + last_issue.should include("foo") | ||
| 173 | + page.should_not have_content 'baz' | ||
| 174 | + end | ||
| 175 | + end | ||
| 176 | + end | ||
| 177 | + | ||
| 178 | + def first_issue | ||
| 179 | + all("ul.issues-list li").first.text | ||
| 180 | + end | ||
| 181 | + | ||
| 182 | + def last_issue | ||
| 183 | + all("ul.issues-list li").last.text | ||
| 184 | + end | ||
| 98 | end | 185 | end |
| @@ -0,0 +1,251 @@ | @@ -0,0 +1,251 @@ | ||
| 1 | +require 'spec_helper' | ||
| 2 | + | ||
| 3 | +describe "Internal Project Access" do | ||
| 4 | + let(:project) { create(:project_with_code) } | ||
| 5 | + | ||
| 6 | + let(:master) { create(:user) } | ||
| 7 | + let(:guest) { create(:user) } | ||
| 8 | + let(:reporter) { create(:user) } | ||
| 9 | + | ||
| 10 | + before do | ||
| 11 | + # internal project | ||
| 12 | + project.visibility_level = Gitlab::VisibilityLevel::INTERNAL | ||
| 13 | + project.save! | ||
| 14 | + | ||
| 15 | + # full access | ||
| 16 | + project.team << [master, :master] | ||
| 17 | + | ||
| 18 | + # readonly | ||
| 19 | + project.team << [reporter, :reporter] | ||
| 20 | + | ||
| 21 | + end | ||
| 22 | + | ||
| 23 | + describe "Project should be internal" do | ||
| 24 | + subject { project } | ||
| 25 | + | ||
| 26 | + its(:internal?) { should be_true } | ||
| 27 | + end | ||
| 28 | + | ||
| 29 | + describe "GET /:project_path" do | ||
| 30 | + subject { project_path(project) } | ||
| 31 | + | ||
| 32 | + it { should be_allowed_for master } | ||
| 33 | + it { should be_allowed_for reporter } | ||
| 34 | + it { should be_allowed_for :admin } | ||
| 35 | + it { should be_allowed_for guest } | ||
| 36 | + it { should be_allowed_for :user } | ||
| 37 | + it { should be_denied_for :visitor } | ||
| 38 | + end | ||
| 39 | + | ||
| 40 | + describe "GET /:project_path/tree/master" do | ||
| 41 | + subject { project_tree_path(project, project.repository.root_ref) } | ||
| 42 | + | ||
| 43 | + it { should be_allowed_for master } | ||
| 44 | + it { should be_allowed_for reporter } | ||
| 45 | + it { should be_allowed_for :admin } | ||
| 46 | + it { should be_allowed_for guest } | ||
| 47 | + it { should be_allowed_for :user } | ||
| 48 | + it { should be_denied_for :visitor } | ||
| 49 | + end | ||
| 50 | + | ||
| 51 | + describe "GET /:project_path/commits/master" do | ||
| 52 | + subject { project_commits_path(project, project.repository.root_ref, limit: 1) } | ||
| 53 | + | ||
| 54 | + it { should be_allowed_for master } | ||
| 55 | + it { should be_allowed_for reporter } | ||
| 56 | + it { should be_allowed_for :admin } | ||
| 57 | + it { should be_allowed_for guest } | ||
| 58 | + it { should be_allowed_for :user } | ||
| 59 | + it { should be_denied_for :visitor } | ||
| 60 | + end | ||
| 61 | + | ||
| 62 | + describe "GET /:project_path/commit/:sha" do | ||
| 63 | + subject { project_commit_path(project, project.repository.commit) } | ||
| 64 | + | ||
| 65 | + it { should be_allowed_for master } | ||
| 66 | + it { should be_allowed_for reporter } | ||
| 67 | + it { should be_allowed_for :admin } | ||
| 68 | + it { should be_allowed_for guest } | ||
| 69 | + it { should be_allowed_for :user } | ||
| 70 | + it { should be_denied_for :visitor } | ||
| 71 | + end | ||
| 72 | + | ||
| 73 | + describe "GET /:project_path/compare" do | ||
| 74 | + subject { project_compare_index_path(project) } | ||
| 75 | + | ||
| 76 | + it { should be_allowed_for master } | ||
| 77 | + it { should be_allowed_for reporter } | ||
| 78 | + it { should be_allowed_for :admin } | ||
| 79 | + it { should be_allowed_for guest } | ||
| 80 | + it { should be_allowed_for :user } | ||
| 81 | + it { should be_denied_for :visitor } | ||
| 82 | + end | ||
| 83 | + | ||
| 84 | + describe "GET /:project_path/team" do | ||
| 85 | + subject { project_team_index_path(project) } | ||
| 86 | + | ||
| 87 | + it { should be_allowed_for master } | ||
| 88 | + it { should be_denied_for reporter } | ||
| 89 | + it { should be_allowed_for :admin } | ||
| 90 | + it { should be_denied_for guest } | ||
| 91 | + it { should be_denied_for :user } | ||
| 92 | + it { should be_denied_for :visitor } | ||
| 93 | + end | ||
| 94 | + | ||
| 95 | + describe "GET /:project_path/wall" do | ||
| 96 | + subject { project_wall_path(project) } | ||
| 97 | + | ||
| 98 | + it { should be_allowed_for master } | ||
| 99 | + it { should be_allowed_for reporter } | ||
| 100 | + it { should be_allowed_for :admin } | ||
| 101 | + it { should be_allowed_for guest } | ||
| 102 | + it { should be_allowed_for :user } | ||
| 103 | + it { should be_denied_for :visitor } | ||
| 104 | + end | ||
| 105 | + | ||
| 106 | + describe "GET /:project_path/blob" do | ||
| 107 | + before do | ||
| 108 | + commit = project.repository.commit | ||
| 109 | + path = commit.tree.contents.select { |i| i.is_a?(Grit::Blob) }.first.name | ||
| 110 | + @blob_path = project_blob_path(project, File.join(commit.id, path)) | ||
| 111 | + end | ||
| 112 | + | ||
| 113 | + it { @blob_path.should be_allowed_for master } | ||
| 114 | + it { @blob_path.should be_allowed_for reporter } | ||
| 115 | + it { @blob_path.should be_allowed_for :admin } | ||
| 116 | + it { @blob_path.should be_allowed_for guest } | ||
| 117 | + it { @blob_path.should be_allowed_for :user } | ||
| 118 | + it { @blob_path.should be_denied_for :visitor } | ||
| 119 | + end | ||
| 120 | + | ||
| 121 | + describe "GET /:project_path/edit" do | ||
| 122 | + subject { edit_project_path(project) } | ||
| 123 | + | ||
| 124 | + it { should be_allowed_for master } | ||
| 125 | + it { should be_denied_for reporter } | ||
| 126 | + it { should be_allowed_for :admin } | ||
| 127 | + it { should be_denied_for guest } | ||
| 128 | + it { should be_denied_for :user } | ||
| 129 | + it { should be_denied_for :visitor } | ||
| 130 | + end | ||
| 131 | + | ||
| 132 | + describe "GET /:project_path/deploy_keys" do | ||
| 133 | + subject { project_deploy_keys_path(project) } | ||
| 134 | + | ||
| 135 | + it { should be_allowed_for master } | ||
| 136 | + it { should be_denied_for reporter } | ||
| 137 | + it { should be_allowed_for :admin } | ||
| 138 | + it { should be_denied_for guest } | ||
| 139 | + it { should be_denied_for :user } | ||
| 140 | + it { should be_denied_for :visitor } | ||
| 141 | + end | ||
| 142 | + | ||
| 143 | + describe "GET /:project_path/issues" do | ||
| 144 | + subject { project_issues_path(project) } | ||
| 145 | + | ||
| 146 | + it { should be_allowed_for master } | ||
| 147 | + it { should be_allowed_for reporter } | ||
| 148 | + it { should be_allowed_for :admin } | ||
| 149 | + it { should be_allowed_for guest } | ||
| 150 | + it { should be_allowed_for :user } | ||
| 151 | + it { should be_denied_for :visitor } | ||
| 152 | + end | ||
| 153 | + | ||
| 154 | + describe "GET /:project_path/snippets" do | ||
| 155 | + subject { project_snippets_path(project) } | ||
| 156 | + | ||
| 157 | + it { should be_allowed_for master } | ||
| 158 | + it { should be_allowed_for reporter } | ||
| 159 | + it { should be_allowed_for :admin } | ||
| 160 | + it { should be_allowed_for guest } | ||
| 161 | + it { should be_allowed_for :user } | ||
| 162 | + it { should be_denied_for :visitor } | ||
| 163 | + end | ||
| 164 | + | ||
| 165 | + describe "GET /:project_path/snippets/new" do | ||
| 166 | + subject { new_project_snippet_path(project) } | ||
| 167 | + | ||
| 168 | + it { should be_allowed_for master } | ||
| 169 | + it { should be_allowed_for reporter } | ||
| 170 | + it { should be_allowed_for :admin } | ||
| 171 | + it { should be_denied_for guest } | ||
| 172 | + it { should be_denied_for :user } | ||
| 173 | + it { should be_denied_for :visitor } | ||
| 174 | + end | ||
| 175 | + | ||
| 176 | + describe "GET /:project_path/merge_requests" do | ||
| 177 | + subject { project_merge_requests_path(project) } | ||
| 178 | + | ||
| 179 | + it { should be_allowed_for master } | ||
| 180 | + it { should be_allowed_for reporter } | ||
| 181 | + it { should be_allowed_for :admin } | ||
| 182 | + it { should be_allowed_for guest } | ||
| 183 | + it { should be_allowed_for :user } | ||
| 184 | + it { should be_denied_for :visitor } | ||
| 185 | + end | ||
| 186 | + | ||
| 187 | + describe "GET /:project_path/merge_requests/new" do | ||
| 188 | + subject { new_project_merge_request_path(project) } | ||
| 189 | + | ||
| 190 | + it { should be_allowed_for master } | ||
| 191 | + it { should be_denied_for reporter } | ||
| 192 | + it { should be_allowed_for :admin } | ||
| 193 | + it { should be_denied_for guest } | ||
| 194 | + it { should be_denied_for :user } | ||
| 195 | + it { should be_denied_for :visitor } | ||
| 196 | + end | ||
| 197 | + | ||
| 198 | + describe "GET /:project_path/branches/recent" do | ||
| 199 | + subject { recent_project_branches_path(project) } | ||
| 200 | + | ||
| 201 | + it { should be_allowed_for master } | ||
| 202 | + it { should be_allowed_for reporter } | ||
| 203 | + it { should be_allowed_for :admin } | ||
| 204 | + it { should be_allowed_for guest } | ||
| 205 | + it { should be_allowed_for :user } | ||
| 206 | + it { should be_denied_for :visitor } | ||
| 207 | + end | ||
| 208 | + | ||
| 209 | + describe "GET /:project_path/branches" do | ||
| 210 | + subject { project_branches_path(project) } | ||
| 211 | + | ||
| 212 | + before do | ||
| 213 | + # Speed increase | ||
| 214 | + Project.any_instance.stub(:branches).and_return([]) | ||
| 215 | + end | ||
| 216 | + | ||
| 217 | + it { should be_allowed_for master } | ||
| 218 | + it { should be_allowed_for reporter } | ||
| 219 | + it { should be_allowed_for :admin } | ||
| 220 | + it { should be_allowed_for guest } | ||
| 221 | + it { should be_allowed_for :user } | ||
| 222 | + it { should be_denied_for :visitor } | ||
| 223 | + end | ||
| 224 | + | ||
| 225 | + describe "GET /:project_path/tags" do | ||
| 226 | + subject { project_tags_path(project) } | ||
| 227 | + | ||
| 228 | + before do | ||
| 229 | + # Speed increase | ||
| 230 | + Project.any_instance.stub(:tags).and_return([]) | ||
| 231 | + end | ||
| 232 | + | ||
| 233 | + it { should be_allowed_for master } | ||
| 234 | + it { should be_allowed_for reporter } | ||
| 235 | + it { should be_allowed_for :admin } | ||
| 236 | + it { should be_allowed_for guest } | ||
| 237 | + it { should be_allowed_for :user } | ||
| 238 | + it { should be_denied_for :visitor } | ||
| 239 | + end | ||
| 240 | + | ||
| 241 | + describe "GET /:project_path/hooks" do | ||
| 242 | + subject { project_hooks_path(project) } | ||
| 243 | + | ||
| 244 | + it { should be_allowed_for master } | ||
| 245 | + it { should be_denied_for reporter } | ||
| 246 | + it { should be_allowed_for :admin } | ||
| 247 | + it { should be_denied_for guest } | ||
| 248 | + it { should be_denied_for :user } | ||
| 249 | + it { should be_denied_for :visitor } | ||
| 250 | + end | ||
| 251 | +end |
spec/features/security/project/private_access_spec.rb
| @@ -15,6 +15,12 @@ describe "Private Project Access" do | @@ -15,6 +15,12 @@ describe "Private Project Access" do | ||
| 15 | project.team << [reporter, :reporter] | 15 | project.team << [reporter, :reporter] |
| 16 | end | 16 | end |
| 17 | 17 | ||
| 18 | + describe "Project should be private" do | ||
| 19 | + subject { project } | ||
| 20 | + | ||
| 21 | + its(:private?) { should be_true } | ||
| 22 | + end | ||
| 23 | + | ||
| 18 | describe "GET /:project_path" do | 24 | describe "GET /:project_path" do |
| 19 | subject { project_path(project) } | 25 | subject { project_path(project) } |
| 20 | 26 |
spec/features/security/project/public_access_spec.rb
| @@ -9,7 +9,7 @@ describe "Public Project Access" do | @@ -9,7 +9,7 @@ describe "Public Project Access" do | ||
| 9 | 9 | ||
| 10 | before do | 10 | before do |
| 11 | # public project | 11 | # public project |
| 12 | - project.public = true | 12 | + project.visibility_level = Gitlab::VisibilityLevel::PUBLIC |
| 13 | project.save! | 13 | project.save! |
| 14 | 14 | ||
| 15 | # full access | 15 | # full access |
spec/helpers/gitlab_markdown_helper_spec.rb
| @@ -378,9 +378,10 @@ describe GitlabMarkdownHelper do | @@ -378,9 +378,10 @@ describe GitlabMarkdownHelper do | ||
| 378 | it "should leave code blocks untouched" do | 378 | it "should leave code blocks untouched" do |
| 379 | helper.stub(:user_color_scheme_class).and_return(:white) | 379 | helper.stub(:user_color_scheme_class).and_return(:white) |
| 380 | 380 | ||
| 381 | - helper.markdown("\n some code from $#{snippet.id}\n here too\n").should include("<div class=\"white\"><div class=\"highlight\"><pre><span class=\"n\">some</span> <span class=\"n\">code</span> <span class=\"n\">from</span> $#{snippet.id}\n<span class=\"n\">here</span> <span class=\"n\">too</span>\n</pre></div></div>") | 381 | + target_html = "<div class=\"white\"><div class=\"highlight\"><pre><span class=\"n\">some</span> <span class=\"n\">code</span> <span class=\"n\">from</span> <span class=\"err\">$</span><span class=\"mi\">#{snippet.id}</span>" |
| 382 | 382 | ||
| 383 | - helper.markdown("\n```\nsome code from $#{snippet.id}\nhere too\n```\n").should include("<div class=\"white\"><div class=\"highlight\"><pre><span class=\"n\">some</span> <span class=\"n\">code</span> <span class=\"n\">from</span> $#{snippet.id}\n<span class=\"n\">here</span> <span class=\"n\">too</span>\n</pre></div></div>") | 383 | + helper.markdown("\n some code from $#{snippet.id}\n here too\n").should include(target_html) |
| 384 | + helper.markdown("\n```\nsome code from $#{snippet.id}\nhere too\n```\n").should include(target_html) | ||
| 384 | end | 385 | end |
| 385 | 386 | ||
| 386 | it "should leave inline code untouched" do | 387 | it "should leave inline code untouched" do |
spec/lib/auth_spec.rb
| @@ -8,21 +8,21 @@ describe Gitlab::Auth do | @@ -8,21 +8,21 @@ describe Gitlab::Auth do | ||
| 8 | @user = create( | 8 | @user = create( |
| 9 | :user, | 9 | :user, |
| 10 | username: 'john', | 10 | username: 'john', |
| 11 | - password: '888777', | ||
| 12 | - password_confirmation: '888777' | 11 | + password: '88877711', |
| 12 | + password_confirmation: '88877711' | ||
| 13 | ) | 13 | ) |
| 14 | end | 14 | end |
| 15 | 15 | ||
| 16 | it "should find user by valid login/password" do | 16 | it "should find user by valid login/password" do |
| 17 | - gl_auth.find('john', '888777').should == @user | 17 | + gl_auth.find('john', '88877711').should == @user |
| 18 | end | 18 | end |
| 19 | 19 | ||
| 20 | it "should not find user with invalid password" do | 20 | it "should not find user with invalid password" do |
| 21 | - gl_auth.find('john', 'invalid').should_not == @user | 21 | + gl_auth.find('john', 'invalid11').should_not == @user |
| 22 | end | 22 | end |
| 23 | 23 | ||
| 24 | it "should not find user with invalid login and password" do | 24 | it "should not find user with invalid login and password" do |
| 25 | - gl_auth.find('jon', 'invalid').should_not == @user | 25 | + gl_auth.find('jon', 'invalid11').should_not == @user |
| 26 | end | 26 | end |
| 27 | end | 27 | end |
| 28 | end | 28 | end |
spec/mailers/notify_spec.rb
| @@ -110,7 +110,7 @@ describe Notify do | @@ -110,7 +110,7 @@ describe Notify do | ||
| 110 | it_behaves_like 'an assignee email' | 110 | it_behaves_like 'an assignee email' |
| 111 | 111 | ||
| 112 | it 'has the correct subject' do | 112 | it 'has the correct subject' do |
| 113 | - should have_subject /#{project.name} \| new issue ##{issue.iid} \| #{issue.title}/ | 113 | + should have_subject /#{project.name} \| New issue ##{issue.iid} \| #{issue.title}/ |
| 114 | end | 114 | end |
| 115 | 115 | ||
| 116 | it 'contains a link to the new issue' do | 116 | it 'contains a link to the new issue' do |
| @@ -126,7 +126,7 @@ describe Notify do | @@ -126,7 +126,7 @@ describe Notify do | ||
| 126 | it_behaves_like 'a multiple recipients email' | 126 | it_behaves_like 'a multiple recipients email' |
| 127 | 127 | ||
| 128 | it 'has the correct subject' do | 128 | it 'has the correct subject' do |
| 129 | - should have_subject /changed issue ##{issue.iid} \| #{issue.title}/ | 129 | + should have_subject /Changed issue ##{issue.iid} \| #{issue.title}/ |
| 130 | end | 130 | end |
| 131 | 131 | ||
| 132 | it 'contains the name of the previous assignee' do | 132 | it 'contains the name of the previous assignee' do |
| @@ -148,7 +148,7 @@ describe Notify do | @@ -148,7 +148,7 @@ describe Notify do | ||
| 148 | subject { Notify.issue_status_changed_email(recipient.id, issue.id, status, current_user) } | 148 | subject { Notify.issue_status_changed_email(recipient.id, issue.id, status, current_user) } |
| 149 | 149 | ||
| 150 | it 'has the correct subject' do | 150 | it 'has the correct subject' do |
| 151 | - should have_subject /changed issue ##{issue.iid} \| #{issue.title}/i | 151 | + should have_subject /Changed issue ##{issue.iid} \| #{issue.title}/i |
| 152 | end | 152 | end |
| 153 | 153 | ||
| 154 | it 'contains the new status' do | 154 | it 'contains the new status' do |
| @@ -175,7 +175,7 @@ describe Notify do | @@ -175,7 +175,7 @@ describe Notify do | ||
| 175 | it_behaves_like 'an assignee email' | 175 | it_behaves_like 'an assignee email' |
| 176 | 176 | ||
| 177 | it 'has the correct subject' do | 177 | it 'has the correct subject' do |
| 178 | - should have_subject /new merge request !#{merge_request.iid}/ | 178 | + should have_subject /New merge request ##{merge_request.iid}/ |
| 179 | end | 179 | end |
| 180 | 180 | ||
| 181 | it 'contains a link to the new merge request' do | 181 | it 'contains a link to the new merge request' do |
| @@ -199,7 +199,7 @@ describe Notify do | @@ -199,7 +199,7 @@ describe Notify do | ||
| 199 | it_behaves_like 'a multiple recipients email' | 199 | it_behaves_like 'a multiple recipients email' |
| 200 | 200 | ||
| 201 | it 'has the correct subject' do | 201 | it 'has the correct subject' do |
| 202 | - should have_subject /changed merge request !#{merge_request.iid}/ | 202 | + should have_subject /Changed merge request ##{merge_request.iid}/ |
| 203 | end | 203 | end |
| 204 | 204 | ||
| 205 | it 'contains the name of the previous assignee' do | 205 | it 'contains the name of the previous assignee' do |
| @@ -224,7 +224,7 @@ describe Notify do | @@ -224,7 +224,7 @@ describe Notify do | ||
| 224 | subject { Notify.project_was_moved_email(project.id, user.id) } | 224 | subject { Notify.project_was_moved_email(project.id, user.id) } |
| 225 | 225 | ||
| 226 | it 'has the correct subject' do | 226 | it 'has the correct subject' do |
| 227 | - should have_subject /project was moved/ | 227 | + should have_subject /Project was moved/ |
| 228 | end | 228 | end |
| 229 | 229 | ||
| 230 | it 'contains name of project' do | 230 | it 'contains name of project' do |
| @@ -244,7 +244,7 @@ describe Notify do | @@ -244,7 +244,7 @@ describe Notify do | ||
| 244 | user: user) } | 244 | user: user) } |
| 245 | subject { Notify.project_access_granted_email(users_project.id) } | 245 | subject { Notify.project_access_granted_email(users_project.id) } |
| 246 | it 'has the correct subject' do | 246 | it 'has the correct subject' do |
| 247 | - should have_subject /access to project was granted/ | 247 | + should have_subject /Access to project was granted/ |
| 248 | end | 248 | end |
| 249 | it 'contains name of project' do | 249 | it 'contains name of project' do |
| 250 | should have_body_text /#{project.name}/ | 250 | should have_body_text /#{project.name}/ |
| @@ -302,7 +302,7 @@ describe Notify do | @@ -302,7 +302,7 @@ describe Notify do | ||
| 302 | it_behaves_like 'a note email' | 302 | it_behaves_like 'a note email' |
| 303 | 303 | ||
| 304 | it 'has the correct subject' do | 304 | it 'has the correct subject' do |
| 305 | - should have_subject /note for commit #{commit.short_id}/ | 305 | + should have_subject /Note for commit #{commit.short_id}/ |
| 306 | end | 306 | end |
| 307 | 307 | ||
| 308 | it 'contains a link to the commit' do | 308 | it 'contains a link to the commit' do |
| @@ -320,7 +320,7 @@ describe Notify do | @@ -320,7 +320,7 @@ describe Notify do | ||
| 320 | it_behaves_like 'a note email' | 320 | it_behaves_like 'a note email' |
| 321 | 321 | ||
| 322 | it 'has the correct subject' do | 322 | it 'has the correct subject' do |
| 323 | - should have_subject /note for merge request ##{merge_request.iid}/ | 323 | + should have_subject /Note for merge request ##{merge_request.iid}/ |
| 324 | end | 324 | end |
| 325 | 325 | ||
| 326 | it 'contains a link to the merge request note' do | 326 | it 'contains a link to the merge request note' do |
| @@ -338,7 +338,7 @@ describe Notify do | @@ -338,7 +338,7 @@ describe Notify do | ||
| 338 | it_behaves_like 'a note email' | 338 | it_behaves_like 'a note email' |
| 339 | 339 | ||
| 340 | it 'has the correct subject' do | 340 | it 'has the correct subject' do |
| 341 | - should have_subject /note for issue ##{issue.iid}/ | 341 | + should have_subject /Note for issue ##{issue.iid}/ |
| 342 | end | 342 | end |
| 343 | 343 | ||
| 344 | it 'contains a link to the issue note' do | 344 | it 'contains a link to the issue note' do |
| @@ -356,7 +356,7 @@ describe Notify do | @@ -356,7 +356,7 @@ describe Notify do | ||
| 356 | subject { Notify.group_access_granted_email(membership.id) } | 356 | subject { Notify.group_access_granted_email(membership.id) } |
| 357 | 357 | ||
| 358 | it 'has the correct subject' do | 358 | it 'has the correct subject' do |
| 359 | - should have_subject /access to group was granted/ | 359 | + should have_subject /Access to group was granted/ |
| 360 | end | 360 | end |
| 361 | 361 | ||
| 362 | it 'contains name of project' do | 362 | it 'contains name of project' do |
| @@ -367,4 +367,28 @@ describe Notify do | @@ -367,4 +367,28 @@ describe Notify do | ||
| 367 | should have_body_text /#{membership.human_access}/ | 367 | should have_body_text /#{membership.human_access}/ |
| 368 | end | 368 | end |
| 369 | end | 369 | end |
| 370 | + | ||
| 371 | + describe 'confirmation if email changed' do | ||
| 372 | + let(:example_site_path) { root_path } | ||
| 373 | + let(:user) { create(:user, email: 'old-email@mail.com') } | ||
| 374 | + | ||
| 375 | + before do | ||
| 376 | + user.email = "new-email@mail.com" | ||
| 377 | + user.save | ||
| 378 | + end | ||
| 379 | + | ||
| 380 | + subject { ActionMailer::Base.deliveries.last } | ||
| 381 | + | ||
| 382 | + it 'is sent to the new user' do | ||
| 383 | + should deliver_to 'new-email@mail.com' | ||
| 384 | + end | ||
| 385 | + | ||
| 386 | + it 'has the correct subject' do | ||
| 387 | + should have_subject "Confirmation instructions" | ||
| 388 | + end | ||
| 389 | + | ||
| 390 | + it 'includes a link to the site' do | ||
| 391 | + should have_body_text /#{example_site_path}/ | ||
| 392 | + end | ||
| 393 | + end | ||
| 370 | end | 394 | end |
| @@ -0,0 +1,50 @@ | @@ -0,0 +1,50 @@ | ||
| 1 | +# == Schema Information | ||
| 2 | +# | ||
| 3 | +# Table name: services | ||
| 4 | +# | ||
| 5 | +# id :integer not null, primary key | ||
| 6 | +# type :string(255) | ||
| 7 | +# title :string(255) | ||
| 8 | +# token :string(255) | ||
| 9 | +# project_id :integer not null | ||
| 10 | +# created_at :datetime not null | ||
| 11 | +# updated_at :datetime not null | ||
| 12 | +# active :boolean default(FALSE), not null | ||
| 13 | +# project_url :string(255) | ||
| 14 | +# subdomain :string(255) | ||
| 15 | +# room :string(255) | ||
| 16 | +# | ||
| 17 | + | ||
| 18 | +require 'spec_helper' | ||
| 19 | + | ||
| 20 | +describe AssemblaService do | ||
| 21 | + describe "Associations" do | ||
| 22 | + it { should belong_to :project } | ||
| 23 | + it { should have_one :service_hook } | ||
| 24 | + end | ||
| 25 | + | ||
| 26 | + describe "Execute" do | ||
| 27 | + let(:user) { create(:user) } | ||
| 28 | + let(:project) { create(:project_with_code) } | ||
| 29 | + | ||
| 30 | + before do | ||
| 31 | + @assembla_service = AssemblaService.new | ||
| 32 | + @assembla_service.stub( | ||
| 33 | + project_id: project.id, | ||
| 34 | + project: project, | ||
| 35 | + service_hook: true, | ||
| 36 | + token: 'verySecret' | ||
| 37 | + ) | ||
| 38 | + @sample_data = GitPushService.new.sample_data(project, user) | ||
| 39 | + @api_url = 'https://atlas.assembla.com/spaces/ouposp/github_tool?secret_key=verySecret' | ||
| 40 | + WebMock.stub_request(:post, @api_url) | ||
| 41 | + end | ||
| 42 | + | ||
| 43 | + it "should call FlowDock API" do | ||
| 44 | + @assembla_service.execute(@sample_data) | ||
| 45 | + WebMock.should have_requested(:post, @api_url).with( | ||
| 46 | + body: /#{@sample_data[:before]}.*#{@sample_data[:after]}.*#{project.path}/ | ||
| 47 | + ).once | ||
| 48 | + end | ||
| 49 | + end | ||
| 50 | +end |
| @@ -0,0 +1,37 @@ | @@ -0,0 +1,37 @@ | ||
| 1 | +# == Schema Information | ||
| 2 | +# | ||
| 3 | +# Table name: broadcast_messages | ||
| 4 | +# | ||
| 5 | +# id :integer not null, primary key | ||
| 6 | +# message :text default(""), not null | ||
| 7 | +# starts_at :datetime | ||
| 8 | +# ends_at :datetime | ||
| 9 | +# alert_type :integer | ||
| 10 | +# created_at :datetime not null | ||
| 11 | +# updated_at :datetime not null | ||
| 12 | +# | ||
| 13 | + | ||
| 14 | +require 'spec_helper' | ||
| 15 | + | ||
| 16 | +describe BroadcastMessage do | ||
| 17 | + subject { create(:broadcast_message) } | ||
| 18 | + | ||
| 19 | + it { should be_valid } | ||
| 20 | + | ||
| 21 | + describe :current do | ||
| 22 | + it "should return last message if time match" do | ||
| 23 | + broadcast_message = create(:broadcast_message, starts_at: Time.now.yesterday, ends_at: Time.now.tomorrow) | ||
| 24 | + BroadcastMessage.current.should == broadcast_message | ||
| 25 | + end | ||
| 26 | + | ||
| 27 | + it "should return nil if time not come" do | ||
| 28 | + broadcast_message = create(:broadcast_message, starts_at: Time.now.tomorrow, ends_at: Time.now + 2.days) | ||
| 29 | + BroadcastMessage.current.should be_nil | ||
| 30 | + end | ||
| 31 | + | ||
| 32 | + it "should return nil if time has passed" do | ||
| 33 | + broadcast_message = create(:broadcast_message, starts_at: Time.now - 2.days, ends_at: Time.now.yesterday) | ||
| 34 | + BroadcastMessage.current.should be_nil | ||
| 35 | + end | ||
| 36 | + end | ||
| 37 | +end |
spec/models/flowdock_service_spec.rb
| @@ -11,6 +11,8 @@ | @@ -11,6 +11,8 @@ | ||
| 11 | # updated_at :datetime not null | 11 | # updated_at :datetime not null |
| 12 | # active :boolean default(FALSE), not null | 12 | # active :boolean default(FALSE), not null |
| 13 | # project_url :string(255) | 13 | # project_url :string(255) |
| 14 | +# subdomain :string(255) | ||
| 15 | +# room :string(255) | ||
| 14 | # | 16 | # |
| 15 | 17 | ||
| 16 | require 'spec_helper' | 18 | require 'spec_helper' |
spec/models/merge_request_spec.rb
| @@ -116,13 +116,13 @@ describe MergeRequest do | @@ -116,13 +116,13 @@ describe MergeRequest do | ||
| 116 | end | 116 | end |
| 117 | 117 | ||
| 118 | it 'accesses the set of issues that will be closed on acceptance' do | 118 | it 'accesses the set of issues that will be closed on acceptance' do |
| 119 | - subject.project.default_branch = subject.target_branch | 119 | + subject.project.stub(default_branch: subject.target_branch) |
| 120 | 120 | ||
| 121 | subject.closes_issues.should == [issue0, issue1].sort_by(&:id) | 121 | subject.closes_issues.should == [issue0, issue1].sort_by(&:id) |
| 122 | end | 122 | end |
| 123 | 123 | ||
| 124 | it 'only lists issues as to be closed if it targets the default branch' do | 124 | it 'only lists issues as to be closed if it targets the default branch' do |
| 125 | - subject.project.default_branch = 'master' | 125 | + subject.project.stub(default_branch: 'master') |
| 126 | subject.target_branch = 'something-else' | 126 | subject.target_branch = 'something-else' |
| 127 | 127 | ||
| 128 | subject.closes_issues.should be_empty | 128 | subject.closes_issues.should be_empty |
spec/models/note_spec.rb
| @@ -61,6 +61,11 @@ describe Note do | @@ -61,6 +61,11 @@ describe Note do | ||
| 61 | note.should be_upvote | 61 | note.should be_upvote |
| 62 | end | 62 | end |
| 63 | 63 | ||
| 64 | + it "recognizes a thumbsup emoji as a vote" do | ||
| 65 | + note = build(:votable_note, note: ":thumbsup: for this") | ||
| 66 | + note.should be_upvote | ||
| 67 | + end | ||
| 68 | + | ||
| 64 | it "recognizes a -1 note" do | 69 | it "recognizes a -1 note" do |
| 65 | note = create(:votable_note, note: "-1 for this") | 70 | note = create(:votable_note, note: "-1 for this") |
| 66 | note.should be_downvote | 71 | note.should be_downvote |
| @@ -70,6 +75,11 @@ describe Note do | @@ -70,6 +75,11 @@ describe Note do | ||
| 70 | note = build(:votable_note, note: ":-1: for this") | 75 | note = build(:votable_note, note: ":-1: for this") |
| 71 | note.should be_downvote | 76 | note.should be_downvote |
| 72 | end | 77 | end |
| 78 | + | ||
| 79 | + it "recognizes a thumbsdown emoji as a vote" do | ||
| 80 | + note = build(:votable_note, note: ":thumbsdown: for this") | ||
| 81 | + note.should be_downvote | ||
| 82 | + end | ||
| 73 | end | 83 | end |
| 74 | 84 | ||
| 75 | let(:project) { create(:project) } | 85 | let(:project) { create(:project) } |
spec/models/project_spec.rb
| @@ -9,19 +9,18 @@ | @@ -9,19 +9,18 @@ | ||
| 9 | # created_at :datetime not null | 9 | # created_at :datetime not null |
| 10 | # updated_at :datetime not null | 10 | # updated_at :datetime not null |
| 11 | # creator_id :integer | 11 | # creator_id :integer |
| 12 | -# default_branch :string(255) | ||
| 13 | # issues_enabled :boolean default(TRUE), not null | 12 | # issues_enabled :boolean default(TRUE), not null |
| 14 | # wall_enabled :boolean default(TRUE), not null | 13 | # wall_enabled :boolean default(TRUE), not null |
| 15 | # merge_requests_enabled :boolean default(TRUE), not null | 14 | # merge_requests_enabled :boolean default(TRUE), not null |
| 16 | # wiki_enabled :boolean default(TRUE), not null | 15 | # wiki_enabled :boolean default(TRUE), not null |
| 17 | # namespace_id :integer | 16 | # namespace_id :integer |
| 18 | -# public :boolean default(FALSE), not null | ||
| 19 | # issues_tracker :string(255) default("gitlab"), not null | 17 | # issues_tracker :string(255) default("gitlab"), not null |
| 20 | # issues_tracker_id :string(255) | 18 | # issues_tracker_id :string(255) |
| 21 | # snippets_enabled :boolean default(TRUE), not null | 19 | # snippets_enabled :boolean default(TRUE), not null |
| 22 | # last_activity_at :datetime | 20 | # last_activity_at :datetime |
| 23 | # imported :boolean default(FALSE), not null | 21 | # imported :boolean default(FALSE), not null |
| 24 | # import_url :string(255) | 22 | # import_url :string(255) |
| 23 | +# visibility_level :integer default(0), not null | ||
| 25 | # | 24 | # |
| 26 | 25 | ||
| 27 | require 'spec_helper' | 26 | require 'spec_helper' |
spec/models/service_hook_spec.rb
| @@ -2,13 +2,16 @@ | @@ -2,13 +2,16 @@ | ||
| 2 | # | 2 | # |
| 3 | # Table name: web_hooks | 3 | # Table name: web_hooks |
| 4 | # | 4 | # |
| 5 | -# id :integer not null, primary key | ||
| 6 | -# url :string(255) | ||
| 7 | -# project_id :integer | ||
| 8 | -# created_at :datetime not null | ||
| 9 | -# updated_at :datetime not null | ||
| 10 | -# type :string(255) default("ProjectHook") | ||
| 11 | -# service_id :integer | 5 | +# id :integer not null, primary key |
| 6 | +# url :string(255) | ||
| 7 | +# project_id :integer | ||
| 8 | +# created_at :datetime not null | ||
| 9 | +# updated_at :datetime not null | ||
| 10 | +# type :string(255) default("ProjectHook") | ||
| 11 | +# service_id :integer | ||
| 12 | +# push_events :boolean default(TRUE), not null | ||
| 13 | +# issues_events :boolean default(FALSE), not null | ||
| 14 | +# merge_requests_events :boolean default(FALSE), not null | ||
| 12 | # | 15 | # |
| 13 | 16 | ||
| 14 | require "spec_helper" | 17 | require "spec_helper" |
spec/models/system_hook_spec.rb
| @@ -2,13 +2,16 @@ | @@ -2,13 +2,16 @@ | ||
| 2 | # | 2 | # |
| 3 | # Table name: web_hooks | 3 | # Table name: web_hooks |
| 4 | # | 4 | # |
| 5 | -# id :integer not null, primary key | ||
| 6 | -# url :string(255) | ||
| 7 | -# project_id :integer | ||
| 8 | -# created_at :datetime not null | ||
| 9 | -# updated_at :datetime not null | ||
| 10 | -# type :string(255) default("ProjectHook") | ||
| 11 | -# service_id :integer | 5 | +# id :integer not null, primary key |
| 6 | +# url :string(255) | ||
| 7 | +# project_id :integer | ||
| 8 | +# created_at :datetime not null | ||
| 9 | +# updated_at :datetime not null | ||
| 10 | +# type :string(255) default("ProjectHook") | ||
| 11 | +# service_id :integer | ||
| 12 | +# push_events :boolean default(TRUE), not null | ||
| 13 | +# issues_events :boolean default(FALSE), not null | ||
| 14 | +# merge_requests_events :boolean default(FALSE), not null | ||
| 12 | # | 15 | # |
| 13 | 16 | ||
| 14 | require "spec_helper" | 17 | require "spec_helper" |
spec/models/user_spec.rb
| @@ -36,6 +36,11 @@ | @@ -36,6 +36,11 @@ | ||
| 36 | # notification_level :integer default(1), not null | 36 | # notification_level :integer default(1), not null |
| 37 | # password_expires_at :datetime | 37 | # password_expires_at :datetime |
| 38 | # created_by_id :integer | 38 | # created_by_id :integer |
| 39 | +# avatar :string(255) | ||
| 40 | +# confirmation_token :string(255) | ||
| 41 | +# confirmed_at :datetime | ||
| 42 | +# confirmation_sent_at :datetime | ||
| 43 | +# unconfirmed_email :string(255) | ||
| 39 | # | 44 | # |
| 40 | 45 | ||
| 41 | require 'spec_helper' | 46 | require 'spec_helper' |
| @@ -85,8 +90,8 @@ describe User do | @@ -85,8 +90,8 @@ describe User do | ||
| 85 | end | 90 | end |
| 86 | 91 | ||
| 87 | it "should not generate password by default" do | 92 | it "should not generate password by default" do |
| 88 | - user = create(:user, password: 'abcdefg') | ||
| 89 | - user.password.should == 'abcdefg' | 93 | + user = create(:user, password: 'abcdefghe') |
| 94 | + user.password.should == 'abcdefghe' | ||
| 90 | end | 95 | end |
| 91 | 96 | ||
| 92 | it "should generate password when forcing random password" do | 97 | it "should generate password when forcing random password" do |
spec/models/web_hook_spec.rb
| @@ -2,13 +2,16 @@ | @@ -2,13 +2,16 @@ | ||
| 2 | # | 2 | # |
| 3 | # Table name: web_hooks | 3 | # Table name: web_hooks |
| 4 | # | 4 | # |
| 5 | -# id :integer not null, primary key | ||
| 6 | -# url :string(255) | ||
| 7 | -# project_id :integer | ||
| 8 | -# created_at :datetime not null | ||
| 9 | -# updated_at :datetime not null | ||
| 10 | -# type :string(255) default("ProjectHook") | ||
| 11 | -# service_id :integer | 5 | +# id :integer not null, primary key |
| 6 | +# url :string(255) | ||
| 7 | +# project_id :integer | ||
| 8 | +# created_at :datetime not null | ||
| 9 | +# updated_at :datetime not null | ||
| 10 | +# type :string(255) default("ProjectHook") | ||
| 11 | +# service_id :integer | ||
| 12 | +# push_events :boolean default(TRUE), not null | ||
| 13 | +# issues_events :boolean default(FALSE), not null | ||
| 14 | +# merge_requests_events :boolean default(FALSE), not null | ||
| 12 | # | 15 | # |
| 13 | 16 | ||
| 14 | require 'spec_helper' | 17 | require 'spec_helper' |
spec/observers/merge_request_observer_spec.rb
| @@ -4,7 +4,7 @@ describe MergeRequestObserver do | @@ -4,7 +4,7 @@ describe MergeRequestObserver do | ||
| 4 | let(:some_user) { create :user } | 4 | let(:some_user) { create :user } |
| 5 | let(:assignee) { create :user } | 5 | let(:assignee) { create :user } |
| 6 | let(:author) { create :user } | 6 | let(:author) { create :user } |
| 7 | - let(:mr_mock) { double(:merge_request, id: 42, assignee: assignee, author: author) } | 7 | + let(:mr_mock) { double(:merge_request, id: 42, assignee: assignee, author: author).as_null_object } |
| 8 | let(:assigned_mr) { create(:merge_request, assignee: assignee, author: author, target_project: create(:project)) } | 8 | let(:assigned_mr) { create(:merge_request, assignee: assignee, author: author, target_project: create(:project)) } |
| 9 | let(:unassigned_mr) { create(:merge_request, author: author, target_project: create(:project)) } | 9 | let(:unassigned_mr) { create(:merge_request, author: author, target_project: create(:project)) } |
| 10 | let(:closed_assigned_mr) { create(:closed_merge_request, assignee: assignee, author: author, target_project: create(:project)) } | 10 | let(:closed_assigned_mr) { create(:closed_merge_request, assignee: assignee, author: author, target_project: create(:project)) } |
spec/observers/users_group_observer_spec.rb
| @@ -23,5 +23,10 @@ describe UsersGroupObserver do | @@ -23,5 +23,10 @@ describe UsersGroupObserver do | ||
| 23 | subject.should_receive(:notification) | 23 | subject.should_receive(:notification) |
| 24 | @membership.update_attribute(:group_access, UsersGroup::MASTER) | 24 | @membership.update_attribute(:group_access, UsersGroup::MASTER) |
| 25 | end | 25 | end |
| 26 | + | ||
| 27 | + it "does not send an email when the access level has not changed" do | ||
| 28 | + subject.should_not_receive(:notification) | ||
| 29 | + @membership.update_attribute(:group_access, UsersGroup::OWNER) | ||
| 30 | + end | ||
| 26 | end | 31 | end |
| 27 | end | 32 | end |
| @@ -0,0 +1,115 @@ | @@ -0,0 +1,115 @@ | ||
| 1 | +require 'spec_helper' | ||
| 2 | + | ||
| 3 | +describe API::API do | ||
| 4 | + include ApiHelpers | ||
| 5 | + before(:each) { ActiveRecord::Base.observers.enable(:user_observer) } | ||
| 6 | + after(:each) { ActiveRecord::Base.observers.disable(:user_observer) } | ||
| 7 | + | ||
| 8 | + let(:user) { create(:user) } | ||
| 9 | + let!(:project) { create(:project_with_code, namespace: user.namespace ) } | ||
| 10 | + before { project.team << [user, :developer] } | ||
| 11 | + | ||
| 12 | + describe "POST /projects/:id/repository/files" do | ||
| 13 | + let(:valid_params) { | ||
| 14 | + { | ||
| 15 | + file_path: 'newfile.rb', | ||
| 16 | + branch_name: 'master', | ||
| 17 | + content: 'puts 8', | ||
| 18 | + commit_message: 'Added newfile' | ||
| 19 | + } | ||
| 20 | + } | ||
| 21 | + | ||
| 22 | + it "should create a new file in project repo" do | ||
| 23 | + Gitlab::Satellite::NewFileAction.any_instance.stub( | ||
| 24 | + commit!: true, | ||
| 25 | + ) | ||
| 26 | + | ||
| 27 | + post api("/projects/#{project.id}/repository/files", user), valid_params | ||
| 28 | + response.status.should == 201 | ||
| 29 | + json_response['file_path'].should == 'newfile.rb' | ||
| 30 | + end | ||
| 31 | + | ||
| 32 | + it "should return a 400 bad request if no params given" do | ||
| 33 | + post api("/projects/#{project.id}/repository/files", user) | ||
| 34 | + response.status.should == 400 | ||
| 35 | + end | ||
| 36 | + | ||
| 37 | + it "should return a 400 if satellite fails to create file" do | ||
| 38 | + Gitlab::Satellite::NewFileAction.any_instance.stub( | ||
| 39 | + commit!: false, | ||
| 40 | + ) | ||
| 41 | + | ||
| 42 | + post api("/projects/#{project.id}/repository/files", user), valid_params | ||
| 43 | + response.status.should == 400 | ||
| 44 | + end | ||
| 45 | + end | ||
| 46 | + | ||
| 47 | + describe "PUT /projects/:id/repository/files" do | ||
| 48 | + let(:valid_params) { | ||
| 49 | + { | ||
| 50 | + file_path: 'spec/spec_helper.rb', | ||
| 51 | + branch_name: 'master', | ||
| 52 | + content: 'puts 8', | ||
| 53 | + commit_message: 'Changed file' | ||
| 54 | + } | ||
| 55 | + } | ||
| 56 | + | ||
| 57 | + it "should update existing file in project repo" do | ||
| 58 | + Gitlab::Satellite::EditFileAction.any_instance.stub( | ||
| 59 | + commit!: true, | ||
| 60 | + ) | ||
| 61 | + | ||
| 62 | + put api("/projects/#{project.id}/repository/files", user), valid_params | ||
| 63 | + response.status.should == 200 | ||
| 64 | + json_response['file_path'].should == 'spec/spec_helper.rb' | ||
| 65 | + end | ||
| 66 | + | ||
| 67 | + it "should return a 400 bad request if no params given" do | ||
| 68 | + put api("/projects/#{project.id}/repository/files", user) | ||
| 69 | + response.status.should == 400 | ||
| 70 | + end | ||
| 71 | + | ||
| 72 | + it "should return a 400 if satellite fails to create file" do | ||
| 73 | + Gitlab::Satellite::EditFileAction.any_instance.stub( | ||
| 74 | + commit!: false, | ||
| 75 | + ) | ||
| 76 | + | ||
| 77 | + put api("/projects/#{project.id}/repository/files", user), valid_params | ||
| 78 | + response.status.should == 400 | ||
| 79 | + end | ||
| 80 | + end | ||
| 81 | + | ||
| 82 | + describe "DELETE /projects/:id/repository/files" do | ||
| 83 | + let(:valid_params) { | ||
| 84 | + { | ||
| 85 | + file_path: 'spec/spec_helper.rb', | ||
| 86 | + branch_name: 'master', | ||
| 87 | + commit_message: 'Changed file' | ||
| 88 | + } | ||
| 89 | + } | ||
| 90 | + | ||
| 91 | + it "should delete existing file in project repo" do | ||
| 92 | + Gitlab::Satellite::DeleteFileAction.any_instance.stub( | ||
| 93 | + commit!: true, | ||
| 94 | + ) | ||
| 95 | + | ||
| 96 | + delete api("/projects/#{project.id}/repository/files", user), valid_params | ||
| 97 | + response.status.should == 200 | ||
| 98 | + json_response['file_path'].should == 'spec/spec_helper.rb' | ||
| 99 | + end | ||
| 100 | + | ||
| 101 | + it "should return a 400 bad request if no params given" do | ||
| 102 | + delete api("/projects/#{project.id}/repository/files", user) | ||
| 103 | + response.status.should == 400 | ||
| 104 | + end | ||
| 105 | + | ||
| 106 | + it "should return a 400 if satellite fails to create file" do | ||
| 107 | + Gitlab::Satellite::DeleteFileAction.any_instance.stub( | ||
| 108 | + commit!: false, | ||
| 109 | + ) | ||
| 110 | + | ||
| 111 | + delete api("/projects/#{project.id}/repository/files", user), valid_params | ||
| 112 | + response.status.should == 400 | ||
| 113 | + end | ||
| 114 | + end | ||
| 115 | +end |
| @@ -0,0 +1,31 @@ | @@ -0,0 +1,31 @@ | ||
| 1 | +require 'spec_helper' | ||
| 2 | + | ||
| 3 | +describe API::API do | ||
| 4 | + include ApiHelpers | ||
| 5 | + before(:each) { ActiveRecord::Base.observers.enable(:user_observer) } | ||
| 6 | + after(:each) { ActiveRecord::Base.observers.disable(:user_observer) } | ||
| 7 | + | ||
| 8 | + let(:admin) { create(:admin) } | ||
| 9 | + let!(:group1) { create(:group) } | ||
| 10 | + let!(:group2) { create(:group) } | ||
| 11 | + | ||
| 12 | + describe "GET /namespaces" do | ||
| 13 | + context "when unauthenticated" do | ||
| 14 | + it "should return authentication error" do | ||
| 15 | + get api("/namespaces") | ||
| 16 | + response.status.should == 401 | ||
| 17 | + end | ||
| 18 | + end | ||
| 19 | + | ||
| 20 | + context "when authenticated as admin" do | ||
| 21 | + it "admin: should return an array of all namespaces" do | ||
| 22 | + get api("/namespaces", admin) | ||
| 23 | + response.status.should == 200 | ||
| 24 | + json_response.should be_an Array | ||
| 25 | + | ||
| 26 | + # Admin namespace + 2 group namespaces | ||
| 27 | + json_response.length.should == 3 | ||
| 28 | + end | ||
| 29 | + end | ||
| 30 | + end | ||
| 31 | +end |
| @@ -0,0 +1,132 @@ | @@ -0,0 +1,132 @@ | ||
| 1 | +require 'spec_helper' | ||
| 2 | + | ||
| 3 | +describe API::API, 'ProjectHooks' do | ||
| 4 | + include ApiHelpers | ||
| 5 | + before(:each) { enable_observers } | ||
| 6 | + after(:each) { disable_observers } | ||
| 7 | + | ||
| 8 | + let(:user) { create(:user) } | ||
| 9 | + let(:user3) { create(:user) } | ||
| 10 | + let!(:project) { create(:project_with_code, creator_id: user.id, namespace: user.namespace) } | ||
| 11 | + let!(:hook) { create(:project_hook, project: project, url: "http://example.com") } | ||
| 12 | + | ||
| 13 | + before do | ||
| 14 | + project.team << [user, :master] | ||
| 15 | + project.team << [user3, :developer] | ||
| 16 | + end | ||
| 17 | + | ||
| 18 | + describe "GET /projects/:id/hooks" do | ||
| 19 | + context "authorized user" do | ||
| 20 | + it "should return project hooks" do | ||
| 21 | + get api("/projects/#{project.id}/hooks", user) | ||
| 22 | + response.status.should == 200 | ||
| 23 | + | ||
| 24 | + json_response.should be_an Array | ||
| 25 | + json_response.count.should == 1 | ||
| 26 | + json_response.first['url'].should == "http://example.com" | ||
| 27 | + end | ||
| 28 | + end | ||
| 29 | + | ||
| 30 | + context "unauthorized user" do | ||
| 31 | + it "should not access project hooks" do | ||
| 32 | + get api("/projects/#{project.id}/hooks", user3) | ||
| 33 | + response.status.should == 403 | ||
| 34 | + end | ||
| 35 | + end | ||
| 36 | + end | ||
| 37 | + | ||
| 38 | + describe "GET /projects/:id/hooks/:hook_id" do | ||
| 39 | + context "authorized user" do | ||
| 40 | + it "should return a project hook" do | ||
| 41 | + get api("/projects/#{project.id}/hooks/#{hook.id}", user) | ||
| 42 | + response.status.should == 200 | ||
| 43 | + json_response['url'].should == hook.url | ||
| 44 | + end | ||
| 45 | + | ||
| 46 | + it "should return a 404 error if hook id is not available" do | ||
| 47 | + get api("/projects/#{project.id}/hooks/1234", user) | ||
| 48 | + response.status.should == 404 | ||
| 49 | + end | ||
| 50 | + end | ||
| 51 | + | ||
| 52 | + context "unauthorized user" do | ||
| 53 | + it "should not access an existing hook" do | ||
| 54 | + get api("/projects/#{project.id}/hooks/#{hook.id}", user3) | ||
| 55 | + response.status.should == 403 | ||
| 56 | + end | ||
| 57 | + end | ||
| 58 | + | ||
| 59 | + it "should return a 404 error if hook id is not available" do | ||
| 60 | + get api("/projects/#{project.id}/hooks/1234", user) | ||
| 61 | + response.status.should == 404 | ||
| 62 | + end | ||
| 63 | + end | ||
| 64 | + | ||
| 65 | + describe "POST /projects/:id/hooks" do | ||
| 66 | + it "should add hook to project" do | ||
| 67 | + expect { | ||
| 68 | + post api("/projects/#{project.id}/hooks", user), | ||
| 69 | + url: "http://example.com", issues_events: true | ||
| 70 | + }.to change {project.hooks.count}.by(1) | ||
| 71 | + response.status.should == 201 | ||
| 72 | + end | ||
| 73 | + | ||
| 74 | + it "should return a 400 error if url not given" do | ||
| 75 | + post api("/projects/#{project.id}/hooks", user) | ||
| 76 | + response.status.should == 400 | ||
| 77 | + end | ||
| 78 | + | ||
| 79 | + it "should return a 422 error if url not valid" do | ||
| 80 | + post api("/projects/#{project.id}/hooks", user), "url" => "ftp://example.com" | ||
| 81 | + response.status.should == 422 | ||
| 82 | + end | ||
| 83 | + end | ||
| 84 | + | ||
| 85 | + describe "PUT /projects/:id/hooks/:hook_id" do | ||
| 86 | + it "should update an existing project hook" do | ||
| 87 | + put api("/projects/#{project.id}/hooks/#{hook.id}", user), | ||
| 88 | + url: 'http://example.org', push_events: false | ||
| 89 | + response.status.should == 200 | ||
| 90 | + json_response['url'].should == 'http://example.org' | ||
| 91 | + end | ||
| 92 | + | ||
| 93 | + it "should return 404 error if hook id not found" do | ||
| 94 | + put api("/projects/#{project.id}/hooks/1234", user), url: 'http://example.org' | ||
| 95 | + response.status.should == 404 | ||
| 96 | + end | ||
| 97 | + | ||
| 98 | + it "should return 400 error if url is not given" do | ||
| 99 | + put api("/projects/#{project.id}/hooks/#{hook.id}", user) | ||
| 100 | + response.status.should == 400 | ||
| 101 | + end | ||
| 102 | + | ||
| 103 | + it "should return a 422 error if url is not valid" do | ||
| 104 | + put api("/projects/#{project.id}/hooks/#{hook.id}", user), url: 'ftp://example.com' | ||
| 105 | + response.status.should == 422 | ||
| 106 | + end | ||
| 107 | + end | ||
| 108 | + | ||
| 109 | + describe "DELETE /projects/:id/hooks/:hook_id" do | ||
| 110 | + it "should delete hook from project" do | ||
| 111 | + expect { | ||
| 112 | + delete api("/projects/#{project.id}/hooks/#{hook.id}", user) | ||
| 113 | + }.to change {project.hooks.count}.by(-1) | ||
| 114 | + response.status.should == 200 | ||
| 115 | + end | ||
| 116 | + | ||
| 117 | + it "should return success when deleting hook" do | ||
| 118 | + delete api("/projects/#{project.id}/hooks/#{hook.id}", user) | ||
| 119 | + response.status.should == 200 | ||
| 120 | + end | ||
| 121 | + | ||
| 122 | + it "should return success when deleting non existent hook" do | ||
| 123 | + delete api("/projects/#{project.id}/hooks/42", user) | ||
| 124 | + response.status.should == 200 | ||
| 125 | + end | ||
| 126 | + | ||
| 127 | + it "should return a 405 error if hook id not given" do | ||
| 128 | + delete api("/projects/#{project.id}/hooks", user) | ||
| 129 | + response.status.should == 405 | ||
| 130 | + end | ||
| 131 | + end | ||
| 132 | +end |
spec/requests/api/projects_spec.rb
| @@ -10,7 +10,6 @@ describe API::API do | @@ -10,7 +10,6 @@ describe API::API do | ||
| 10 | let(:user3) { create(:user) } | 10 | let(:user3) { create(:user) } |
| 11 | let(:admin) { create(:admin) } | 11 | let(:admin) { create(:admin) } |
| 12 | let!(:project) { create(:project_with_code, creator_id: user.id, namespace: user.namespace) } | 12 | let!(:project) { create(:project_with_code, creator_id: user.id, namespace: user.namespace) } |
| 13 | - let!(:hook) { create(:project_hook, project: project, url: "http://example.com") } | ||
| 14 | let!(:snippet) { create(:project_snippet, author: user, project: project, title: 'example') } | 13 | let!(:snippet) { create(:project_snippet, author: user, project: project, title: 'example') } |
| 15 | let!(:users_project) { create(:users_project, user: user, project: project, project_access: UsersProject::MASTER) } | 14 | let!(:users_project) { create(:users_project, user: user, project: project, project_access: UsersProject::MASTER) } |
| 16 | let!(:users_project2) { create(:users_project, user: user3, project: project, project_access: UsersProject::DEVELOPER) } | 15 | let!(:users_project2) { create(:users_project, user: user3, project: project, project_access: UsersProject::DEVELOPER) } |
| @@ -36,6 +35,32 @@ describe API::API do | @@ -36,6 +35,32 @@ describe API::API do | ||
| 36 | end | 35 | end |
| 37 | end | 36 | end |
| 38 | 37 | ||
| 38 | + describe "GET /projects/all" do | ||
| 39 | + context "when unauthenticated" do | ||
| 40 | + it "should return authentication error" do | ||
| 41 | + get api("/projects/all") | ||
| 42 | + response.status.should == 401 | ||
| 43 | + end | ||
| 44 | + end | ||
| 45 | + | ||
| 46 | + context "when authenticated as regular user" do | ||
| 47 | + it "should return authentication error" do | ||
| 48 | + get api("/projects/all", user) | ||
| 49 | + response.status.should == 403 | ||
| 50 | + end | ||
| 51 | + end | ||
| 52 | + | ||
| 53 | + context "when authenticated as admin" do | ||
| 54 | + it "should return an array of all projects" do | ||
| 55 | + get api("/projects/all", admin) | ||
| 56 | + response.status.should == 200 | ||
| 57 | + json_response.should be_an Array | ||
| 58 | + json_response.first['name'].should == project.name | ||
| 59 | + json_response.first['owner']['email'].should == user.email | ||
| 60 | + end | ||
| 61 | + end | ||
| 62 | + end | ||
| 63 | + | ||
| 39 | describe "POST /projects" do | 64 | describe "POST /projects" do |
| 40 | context "maximum number of projects reached" do | 65 | context "maximum number of projects reached" do |
| 41 | before do | 66 | before do |
| @@ -91,7 +116,6 @@ describe API::API do | @@ -91,7 +116,6 @@ describe API::API do | ||
| 91 | it "should assign attributes to project" do | 116 | it "should assign attributes to project" do |
| 92 | project = attributes_for(:project, { | 117 | project = attributes_for(:project, { |
| 93 | description: Faker::Lorem.sentence, | 118 | description: Faker::Lorem.sentence, |
| 94 | - default_branch: 'stable', | ||
| 95 | issues_enabled: false, | 119 | issues_enabled: false, |
| 96 | wall_enabled: false, | 120 | wall_enabled: false, |
| 97 | merge_requests_enabled: false, | 121 | merge_requests_enabled: false, |
| @@ -107,19 +131,46 @@ describe API::API do | @@ -107,19 +131,46 @@ describe API::API do | ||
| 107 | end | 131 | end |
| 108 | 132 | ||
| 109 | it "should set a project as public" do | 133 | it "should set a project as public" do |
| 134 | + project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::PUBLIC }) | ||
| 135 | + post api("/projects", user), project | ||
| 136 | + json_response['public'].should be_true | ||
| 137 | + json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC | ||
| 138 | + end | ||
| 139 | + | ||
| 140 | + it "should set a project as public using :public" do | ||
| 110 | project = attributes_for(:project, { public: true }) | 141 | project = attributes_for(:project, { public: true }) |
| 111 | post api("/projects", user), project | 142 | post api("/projects", user), project |
| 112 | json_response['public'].should be_true | 143 | json_response['public'].should be_true |
| 144 | + json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC | ||
| 145 | + end | ||
| 113 | 146 | ||
| 147 | + it "should set a project as internal" do | ||
| 148 | + project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::INTERNAL }) | ||
| 149 | + post api("/projects", user), project | ||
| 150 | + json_response['public'].should be_false | ||
| 151 | + json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL | ||
| 114 | end | 152 | end |
| 115 | 153 | ||
| 116 | - it "should set a project as private" do | ||
| 117 | - project = attributes_for(:project, { public: false }) | 154 | + it "should set a project as internal overriding :public" do |
| 155 | + project = attributes_for(:project, { public: true, visibility_level: Gitlab::VisibilityLevel::INTERNAL }) | ||
| 118 | post api("/projects", user), project | 156 | post api("/projects", user), project |
| 119 | json_response['public'].should be_false | 157 | json_response['public'].should be_false |
| 158 | + json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL | ||
| 159 | + end | ||
| 120 | 160 | ||
| 161 | + it "should set a project as private" do | ||
| 162 | + project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::PRIVATE }) | ||
| 163 | + post api("/projects", user), project | ||
| 164 | + json_response['public'].should be_false | ||
| 165 | + json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE | ||
| 121 | end | 166 | end |
| 122 | 167 | ||
| 168 | + it "should set a project as private using :public" do | ||
| 169 | + project = attributes_for(:project, { public: false }) | ||
| 170 | + post api("/projects", user), project | ||
| 171 | + json_response['public'].should be_false | ||
| 172 | + json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE | ||
| 173 | + end | ||
| 123 | end | 174 | end |
| 124 | 175 | ||
| 125 | describe "POST /projects/user/:id" do | 176 | describe "POST /projects/user/:id" do |
| @@ -146,7 +197,6 @@ describe API::API do | @@ -146,7 +197,6 @@ describe API::API do | ||
| 146 | it "should assign attributes to project" do | 197 | it "should assign attributes to project" do |
| 147 | project = attributes_for(:project, { | 198 | project = attributes_for(:project, { |
| 148 | description: Faker::Lorem.sentence, | 199 | description: Faker::Lorem.sentence, |
| 149 | - default_branch: 'stable', | ||
| 150 | issues_enabled: false, | 200 | issues_enabled: false, |
| 151 | wall_enabled: false, | 201 | wall_enabled: false, |
| 152 | merge_requests_enabled: false, | 202 | merge_requests_enabled: false, |
| @@ -162,19 +212,46 @@ describe API::API do | @@ -162,19 +212,46 @@ describe API::API do | ||
| 162 | end | 212 | end |
| 163 | 213 | ||
| 164 | it "should set a project as public" do | 214 | it "should set a project as public" do |
| 215 | + project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::PUBLIC }) | ||
| 216 | + post api("/projects/user/#{user.id}", admin), project | ||
| 217 | + json_response['public'].should be_true | ||
| 218 | + json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC | ||
| 219 | + end | ||
| 220 | + | ||
| 221 | + it "should set a project as public using :public" do | ||
| 165 | project = attributes_for(:project, { public: true }) | 222 | project = attributes_for(:project, { public: true }) |
| 166 | post api("/projects/user/#{user.id}", admin), project | 223 | post api("/projects/user/#{user.id}", admin), project |
| 167 | json_response['public'].should be_true | 224 | json_response['public'].should be_true |
| 225 | + json_response['visibility_level'].should == Gitlab::VisibilityLevel::PUBLIC | ||
| 226 | + end | ||
| 168 | 227 | ||
| 228 | + it "should set a project as internal" do | ||
| 229 | + project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::INTERNAL }) | ||
| 230 | + post api("/projects/user/#{user.id}", admin), project | ||
| 231 | + json_response['public'].should be_false | ||
| 232 | + json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL | ||
| 169 | end | 233 | end |
| 170 | 234 | ||
| 171 | - it "should set a project as private" do | ||
| 172 | - project = attributes_for(:project, { public: false }) | 235 | + it "should set a project as internal overriding :public" do |
| 236 | + project = attributes_for(:project, { public: true, visibility_level: Gitlab::VisibilityLevel::INTERNAL }) | ||
| 173 | post api("/projects/user/#{user.id}", admin), project | 237 | post api("/projects/user/#{user.id}", admin), project |
| 174 | json_response['public'].should be_false | 238 | json_response['public'].should be_false |
| 239 | + json_response['visibility_level'].should == Gitlab::VisibilityLevel::INTERNAL | ||
| 240 | + end | ||
| 175 | 241 | ||
| 242 | + it "should set a project as private" do | ||
| 243 | + project = attributes_for(:project, { visibility_level: Gitlab::VisibilityLevel::PRIVATE }) | ||
| 244 | + post api("/projects/user/#{user.id}", admin), project | ||
| 245 | + json_response['public'].should be_false | ||
| 246 | + json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE | ||
| 176 | end | 247 | end |
| 177 | 248 | ||
| 249 | + it "should set a project as private using :public" do | ||
| 250 | + project = attributes_for(:project, { public: false }) | ||
| 251 | + post api("/projects/user/#{user.id}", admin), project | ||
| 252 | + json_response['public'].should be_false | ||
| 253 | + json_response['visibility_level'].should == Gitlab::VisibilityLevel::PRIVATE | ||
| 254 | + end | ||
| 178 | end | 255 | end |
| 179 | 256 | ||
| 180 | describe "GET /projects/:id" do | 257 | describe "GET /projects/:id" do |
| @@ -361,121 +438,6 @@ describe API::API do | @@ -361,121 +438,6 @@ describe API::API do | ||
| 361 | end | 438 | end |
| 362 | end | 439 | end |
| 363 | 440 | ||
| 364 | - describe "GET /projects/:id/hooks" do | ||
| 365 | - context "authorized user" do | ||
| 366 | - it "should return project hooks" do | ||
| 367 | - get api("/projects/#{project.id}/hooks", user) | ||
| 368 | - response.status.should == 200 | ||
| 369 | - | ||
| 370 | - json_response.should be_an Array | ||
| 371 | - json_response.count.should == 1 | ||
| 372 | - json_response.first['url'].should == "http://example.com" | ||
| 373 | - end | ||
| 374 | - end | ||
| 375 | - | ||
| 376 | - context "unauthorized user" do | ||
| 377 | - it "should not access project hooks" do | ||
| 378 | - get api("/projects/#{project.id}/hooks", user3) | ||
| 379 | - response.status.should == 403 | ||
| 380 | - end | ||
| 381 | - end | ||
| 382 | - end | ||
| 383 | - | ||
| 384 | - describe "GET /projects/:id/hooks/:hook_id" do | ||
| 385 | - context "authorized user" do | ||
| 386 | - it "should return a project hook" do | ||
| 387 | - get api("/projects/#{project.id}/hooks/#{hook.id}", user) | ||
| 388 | - response.status.should == 200 | ||
| 389 | - json_response['url'].should == hook.url | ||
| 390 | - end | ||
| 391 | - | ||
| 392 | - it "should return a 404 error if hook id is not available" do | ||
| 393 | - get api("/projects/#{project.id}/hooks/1234", user) | ||
| 394 | - response.status.should == 404 | ||
| 395 | - end | ||
| 396 | - end | ||
| 397 | - | ||
| 398 | - context "unauthorized user" do | ||
| 399 | - it "should not access an existing hook" do | ||
| 400 | - get api("/projects/#{project.id}/hooks/#{hook.id}", user3) | ||
| 401 | - response.status.should == 403 | ||
| 402 | - end | ||
| 403 | - end | ||
| 404 | - | ||
| 405 | - it "should return a 404 error if hook id is not available" do | ||
| 406 | - get api("/projects/#{project.id}/hooks/1234", user) | ||
| 407 | - response.status.should == 404 | ||
| 408 | - end | ||
| 409 | - end | ||
| 410 | - | ||
| 411 | - describe "POST /projects/:id/hooks" do | ||
| 412 | - it "should add hook to project" do | ||
| 413 | - expect { | ||
| 414 | - post api("/projects/#{project.id}/hooks", user), | ||
| 415 | - url: "http://example.com" | ||
| 416 | - }.to change {project.hooks.count}.by(1) | ||
| 417 | - response.status.should == 201 | ||
| 418 | - end | ||
| 419 | - | ||
| 420 | - it "should return a 400 error if url not given" do | ||
| 421 | - post api("/projects/#{project.id}/hooks", user) | ||
| 422 | - response.status.should == 400 | ||
| 423 | - end | ||
| 424 | - | ||
| 425 | - it "should return a 422 error if url not valid" do | ||
| 426 | - post api("/projects/#{project.id}/hooks", user), "url" => "ftp://example.com" | ||
| 427 | - response.status.should == 422 | ||
| 428 | - end | ||
| 429 | - end | ||
| 430 | - | ||
| 431 | - describe "PUT /projects/:id/hooks/:hook_id" do | ||
| 432 | - it "should update an existing project hook" do | ||
| 433 | - put api("/projects/#{project.id}/hooks/#{hook.id}", user), | ||
| 434 | - url: 'http://example.org' | ||
| 435 | - response.status.should == 200 | ||
| 436 | - json_response['url'].should == 'http://example.org' | ||
| 437 | - end | ||
| 438 | - | ||
| 439 | - it "should return 404 error if hook id not found" do | ||
| 440 | - put api("/projects/#{project.id}/hooks/1234", user), url: 'http://example.org' | ||
| 441 | - response.status.should == 404 | ||
| 442 | - end | ||
| 443 | - | ||
| 444 | - it "should return 400 error if url is not given" do | ||
| 445 | - put api("/projects/#{project.id}/hooks/#{hook.id}", user) | ||
| 446 | - response.status.should == 400 | ||
| 447 | - end | ||
| 448 | - | ||
| 449 | - it "should return a 422 error if url is not valid" do | ||
| 450 | - put api("/projects/#{project.id}/hooks/#{hook.id}", user), url: 'ftp://example.com' | ||
| 451 | - response.status.should == 422 | ||
| 452 | - end | ||
| 453 | - end | ||
| 454 | - | ||
| 455 | - describe "DELETE /projects/:id/hooks/:hook_id" do | ||
| 456 | - it "should delete hook from project" do | ||
| 457 | - expect { | ||
| 458 | - delete api("/projects/#{project.id}/hooks/#{hook.id}", user) | ||
| 459 | - }.to change {project.hooks.count}.by(-1) | ||
| 460 | - response.status.should == 200 | ||
| 461 | - end | ||
| 462 | - | ||
| 463 | - it "should return success when deleting hook" do | ||
| 464 | - delete api("/projects/#{project.id}/hooks/#{hook.id}", user) | ||
| 465 | - response.status.should == 200 | ||
| 466 | - end | ||
| 467 | - | ||
| 468 | - it "should return success when deleting non existent hook" do | ||
| 469 | - delete api("/projects/#{project.id}/hooks/42", user) | ||
| 470 | - response.status.should == 200 | ||
| 471 | - end | ||
| 472 | - | ||
| 473 | - it "should return a 405 error if hook id not given" do | ||
| 474 | - delete api("/projects/#{project.id}/hooks", user) | ||
| 475 | - response.status.should == 405 | ||
| 476 | - end | ||
| 477 | - end | ||
| 478 | - | ||
| 479 | describe "GET /projects/:id/snippets" do | 441 | describe "GET /projects/:id/snippets" do |
| 480 | it "should return an array of project snippets" do | 442 | it "should return an array of project snippets" do |
| 481 | get api("/projects/#{project.id}/snippets", user) | 443 | get api("/projects/#{project.id}/snippets", user) |
| @@ -628,10 +590,10 @@ describe API::API do | @@ -628,10 +590,10 @@ describe API::API do | ||
| 628 | 590 | ||
| 629 | describe :fork_admin do | 591 | describe :fork_admin do |
| 630 | let(:project_fork_target) { create(:project) } | 592 | let(:project_fork_target) { create(:project) } |
| 631 | - let(:project_fork_source) { create(:project, public: true) } | 593 | + let(:project_fork_source) { create(:project, visibility_level: Gitlab::VisibilityLevel::PUBLIC) } |
| 632 | 594 | ||
| 633 | describe "POST /projects/:id/fork/:forked_from_id" do | 595 | describe "POST /projects/:id/fork/:forked_from_id" do |
| 634 | - let(:new_project_fork_source) { create(:project, public: true) } | 596 | + let(:new_project_fork_source) { create(:project, visibility_level: Gitlab::VisibilityLevel::PUBLIC) } |
| 635 | 597 | ||
| 636 | it "shouldn't available for non admin users" do | 598 | it "shouldn't available for non admin users" do |
| 637 | post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user) | 599 | post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user) |
| @@ -700,8 +662,10 @@ describe API::API do | @@ -700,8 +662,10 @@ describe API::API do | ||
| 700 | let!(:post) { create(:project, name: "#{query}_post", creator_id: user.id, namespace: user.namespace) } | 662 | let!(:post) { create(:project, name: "#{query}_post", creator_id: user.id, namespace: user.namespace) } |
| 701 | let!(:pre_post) { create(:project, name: "pre_#{query}_post", creator_id: user.id, namespace: user.namespace) } | 663 | let!(:pre_post) { create(:project, name: "pre_#{query}_post", creator_id: user.id, namespace: user.namespace) } |
| 702 | let!(:unfound) { create(:project, name: 'unfound', creator_id: user.id, namespace: user.namespace) } | 664 | let!(:unfound) { create(:project, name: 'unfound', creator_id: user.id, namespace: user.namespace) } |
| 703 | - let!(:public) { create(:project, name: "another #{query}",public: true) } | ||
| 704 | - let!(:unfound_public) { create(:project, name: 'unfound public', public: true) } | 665 | + let!(:internal) { create(:project, name: "internal #{query}", visibility_level: Gitlab::VisibilityLevel::INTERNAL) } |
| 666 | + let!(:unfound_internal) { create(:project, name: 'unfound internal', visibility_level: Gitlab::VisibilityLevel::INTERNAL) } | ||
| 667 | + let!(:public) { create(:project, name: "public #{query}", visibility_level: Gitlab::VisibilityLevel::PUBLIC) } | ||
| 668 | + let!(:unfound_public) { create(:project, name: 'unfound public', visibility_level: Gitlab::VisibilityLevel::PUBLIC) } | ||
| 705 | 669 | ||
| 706 | context "when unauthenticated" do | 670 | context "when unauthenticated" do |
| 707 | it "should return authentication error" do | 671 | it "should return authentication error" do |
| @@ -715,7 +679,7 @@ describe API::API do | @@ -715,7 +679,7 @@ describe API::API do | ||
| 715 | get api("/projects/search/#{query}",user) | 679 | get api("/projects/search/#{query}",user) |
| 716 | response.status.should == 200 | 680 | response.status.should == 200 |
| 717 | json_response.should be_an Array | 681 | json_response.should be_an Array |
| 718 | - json_response.size.should == 5 | 682 | + json_response.size.should == 6 |
| 719 | json_response.each {|project| project['name'].should =~ /.*query.*/} | 683 | json_response.each {|project| project['name'].should =~ /.*query.*/} |
| 720 | end | 684 | end |
| 721 | end | 685 | end |
| @@ -725,8 +689,8 @@ describe API::API do | @@ -725,8 +689,8 @@ describe API::API do | ||
| 725 | get api("/projects/search/#{query}", user2) | 689 | get api("/projects/search/#{query}", user2) |
| 726 | response.status.should == 200 | 690 | response.status.should == 200 |
| 727 | json_response.should be_an Array | 691 | json_response.should be_an Array |
| 728 | - json_response.size.should == 1 | ||
| 729 | - json_response.first['name'].should == "another #{query}" | 692 | + json_response.size.should == 2 |
| 693 | + json_response.each {|project| project['name'].should =~ /(internal|public) query/} | ||
| 730 | end | 694 | end |
| 731 | end | 695 | end |
| 732 | end | 696 | end |
spec/requests/api/session_spec.rb
| @@ -8,7 +8,7 @@ describe API::API do | @@ -8,7 +8,7 @@ describe API::API do | ||
| 8 | describe "POST /session" do | 8 | describe "POST /session" do |
| 9 | context "when valid password" do | 9 | context "when valid password" do |
| 10 | it "should return private token" do | 10 | it "should return private token" do |
| 11 | - post api("/session"), email: user.email, password: '123456' | 11 | + post api("/session"), email: user.email, password: '12345678' |
| 12 | response.status.should == 201 | 12 | response.status.should == 201 |
| 13 | 13 | ||
| 14 | json_response['email'].should == user.email | 14 | json_response['email'].should == user.email |
spec/routing/routing_spec.rb
| @@ -185,6 +185,13 @@ describe Profiles::KeysController, "routing" do | @@ -185,6 +185,13 @@ describe Profiles::KeysController, "routing" do | ||
| 185 | end | 185 | end |
| 186 | end | 186 | end |
| 187 | 187 | ||
| 188 | +# profile_avatar DELETE /profile/avatar(.:format) profiles/avatars#destroy | ||
| 189 | +describe Profiles::AvatarsController, "routing" do | ||
| 190 | + it "to #destroy" do | ||
| 191 | + delete("/profile/avatar").should route_to('profiles/avatars#destroy') | ||
| 192 | + end | ||
| 193 | +end | ||
| 194 | + | ||
| 188 | # dashboard GET /dashboard(.:format) dashboard#show | 195 | # dashboard GET /dashboard(.:format) dashboard#show |
| 189 | # dashboard_issues GET /dashboard/issues(.:format) dashboard#issues | 196 | # dashboard_issues GET /dashboard/issues(.:format) dashboard#issues |
| 190 | # dashboard_merge_requests GET /dashboard/merge_requests(.:format) dashboard#merge_requests | 197 | # dashboard_merge_requests GET /dashboard/merge_requests(.:format) dashboard#merge_requests |
spec/services/git_push_service_spec.rb
| @@ -74,38 +74,19 @@ describe GitPushService do | @@ -74,38 +74,19 @@ describe GitPushService do | ||
| 74 | end | 74 | end |
| 75 | 75 | ||
| 76 | describe "Web Hooks" do | 76 | describe "Web Hooks" do |
| 77 | - context "with web hooks" do | ||
| 78 | - before do | ||
| 79 | - @project_hook = create(:project_hook) | ||
| 80 | - @project_hook_2 = create(:project_hook) | ||
| 81 | - project.hooks << [@project_hook, @project_hook_2] | ||
| 82 | - | ||
| 83 | - stub_request(:post, @project_hook.url) | ||
| 84 | - stub_request(:post, @project_hook_2.url) | ||
| 85 | - end | ||
| 86 | - | ||
| 87 | - it "executes multiple web hook" do | ||
| 88 | - @project_hook.should_receive(:async_execute).once | ||
| 89 | - @project_hook_2.should_receive(:async_execute).once | ||
| 90 | - | ||
| 91 | - service.execute(project, user, @oldrev, @newrev, @ref) | ||
| 92 | - end | ||
| 93 | - end | ||
| 94 | - | ||
| 95 | context "execute web hooks" do | 77 | context "execute web hooks" do |
| 96 | - before do | ||
| 97 | - @project_hook = create(:project_hook) | ||
| 98 | - project.hooks << [@project_hook] | ||
| 99 | - stub_request(:post, @project_hook.url) | ||
| 100 | - end | ||
| 101 | - | ||
| 102 | it "when pushing a branch for the first time" do | 78 | it "when pushing a branch for the first time" do |
| 103 | - @project_hook.should_receive(:async_execute) | 79 | + project.should_receive(:execute_hooks) |
| 104 | service.execute(project, user, @blankrev, 'newrev', 'refs/heads/master') | 80 | service.execute(project, user, @blankrev, 'newrev', 'refs/heads/master') |
| 105 | end | 81 | end |
| 106 | 82 | ||
| 83 | + it "when pushing new commits to existing branch" do | ||
| 84 | + project.should_receive(:execute_hooks) | ||
| 85 | + service.execute(project, user, 'oldrev', 'newrev', 'refs/heads/master') | ||
| 86 | + end | ||
| 87 | + | ||
| 107 | it "when pushing tags" do | 88 | it "when pushing tags" do |
| 108 | - @project_hook.should_not_receive(:async_execute) | 89 | + project.should_not_receive(:execute_hooks) |
| 109 | service.execute(project, user, 'newrev', 'newrev', 'refs/tags/v1.0.0') | 90 | service.execute(project, user, 'newrev', 'newrev', 'refs/tags/v1.0.0') |
| 110 | end | 91 | end |
| 111 | end | 92 | end |
spec/support/login_helpers.rb
| @@ -16,7 +16,7 @@ module LoginHelpers | @@ -16,7 +16,7 @@ module LoginHelpers | ||
| 16 | def login_with(user) | 16 | def login_with(user) |
| 17 | visit new_user_session_path | 17 | visit new_user_session_path |
| 18 | fill_in "user_login", with: user.email | 18 | fill_in "user_login", with: user.email |
| 19 | - fill_in "user_password", with: "123456" | 19 | + fill_in "user_password", with: "12345678" |
| 20 | click_button "Sign in" | 20 | click_button "Sign in" |
| 21 | Thread.current[:current_user] = user | 21 | Thread.current[:current_user] = user |
| 22 | end | 22 | end |
spec/support/test_env.rb
| @@ -45,6 +45,7 @@ module TestEnv | @@ -45,6 +45,7 @@ module TestEnv | ||
| 45 | def disable_mailer | 45 | def disable_mailer |
| 46 | NotificationService.any_instance.stub(mailer: double.as_null_object) | 46 | NotificationService.any_instance.stub(mailer: double.as_null_object) |
| 47 | end | 47 | end |
| 48 | + | ||
| 48 | def enable_mailer | 49 | def enable_mailer |
| 49 | NotificationService.any_instance.unstub(:mailer) | 50 | NotificationService.any_instance.unstub(:mailer) |
| 50 | end | 51 | end |
| @@ -68,7 +69,8 @@ module TestEnv | @@ -68,7 +69,8 @@ module TestEnv | ||
| 68 | remove_repository: true, | 69 | remove_repository: true, |
| 69 | update_repository_head: true, | 70 | update_repository_head: true, |
| 70 | add_key: true, | 71 | add_key: true, |
| 71 | - remove_key: true | 72 | + remove_key: true, |
| 73 | + version: '6.3.0' | ||
| 72 | ) | 74 | ) |
| 73 | 75 | ||
| 74 | Gitlab::Satellite::Satellite.any_instance.stub( | 76 | Gitlab::Satellite::Satellite.any_instance.stub( |
| @@ -96,6 +98,15 @@ module TestEnv | @@ -96,6 +98,15 @@ module TestEnv | ||
| 96 | FileUtils.rm_rf File.join(testing_path(), "#{name}.wiki.git") | 98 | FileUtils.rm_rf File.join(testing_path(), "#{name}.wiki.git") |
| 97 | end | 99 | end |
| 98 | 100 | ||
| 101 | + def reset_satellite_dir | ||
| 102 | + setup_stubs | ||
| 103 | + FileUtils.cd(seed_satellite_path) do | ||
| 104 | + `git reset --hard --quiet` | ||
| 105 | + `git clean -fx` | ||
| 106 | + `git checkout --quiet origin/master` | ||
| 107 | + end | ||
| 108 | + end | ||
| 109 | + | ||
| 99 | # Create a repo and it's satellite | 110 | # Create a repo and it's satellite |
| 100 | def create_repo(namespace, name) | 111 | def create_repo(namespace, name) |
| 101 | setup_stubs | 112 | setup_stubs |
vendor/assets/javascripts/ace-src-noconflict/mode-diff.js
| @@ -66,7 +66,7 @@ var DiffHighlightRules = function() { | @@ -66,7 +66,7 @@ var DiffHighlightRules = function() { | ||
| 66 | "regex": "^(?:\\*{15}|={67}|-{3}|\\+{3})$", | 66 | "regex": "^(?:\\*{15}|={67}|-{3}|\\+{3})$", |
| 67 | "token": "punctuation.definition.separator.diff", | 67 | "token": "punctuation.definition.separator.diff", |
| 68 | "name": "keyword" | 68 | "name": "keyword" |
| 69 | - }, { //diff.range.unified | 69 | + }, { //diff.range.inline |
| 70 | "regex": "^(@@)(\\s*.+?\\s*)(@@)(.*)$", | 70 | "regex": "^(@@)(\\s*.+?\\s*)(@@)(.*)$", |
| 71 | "token": [ | 71 | "token": [ |
| 72 | "constant", | 72 | "constant", |