Commit e016cdb7 authored by Long Nguyen's avatar Long Nguyen

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into...

Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into issue_14532_assign_labels_milestone_when_moving_issue
parents 91f693c0 f5ae92d8
...@@ -953,10 +953,9 @@ Performance/DoubleStartEndWith: ...@@ -953,10 +953,9 @@ Performance/DoubleStartEndWith:
Performance/EndWith: Performance/EndWith:
Enabled: false Enabled: false
# TODO: Enable LstripRstrip Cop.
# Use `strip` instead of `lstrip.rstrip`. # Use `strip` instead of `lstrip.rstrip`.
Performance/LstripRstrip: Performance/LstripRstrip:
Enabled: false Enabled: true
# TODO: Enable RangeInclude Cop. # TODO: Enable RangeInclude Cop.
# Use `Range#cover?` instead of `Range#include?`. # Use `Range#cover?` instead of `Range#include?`.
......
...@@ -244,11 +244,11 @@ linters: ...@@ -244,11 +244,11 @@ linters:
# URLs should be valid and not contain protocols or domain names. # URLs should be valid and not contain protocols or domain names.
UrlFormat: UrlFormat:
enabled: false enabled: true
# URLs should always be enclosed within quotes. # URLs should always be enclosed within quotes.
UrlQuotes: UrlQuotes:
enabled: false enabled: true
# Properties, like color and font, are easier to read and maintain # Properties, like color and font, are easier to read and maintain
# when defined using variables rather than literals. # when defined using variables rather than literals.
......
...@@ -2,18 +2,47 @@ Please view this file on the master branch, on stable branches it's out of date. ...@@ -2,18 +2,47 @@ Please view this file on the master branch, on stable branches it's out of date.
v 8.8.0 (unreleased) v 8.8.0 (unreleased)
- Assign labels and milestone to target project when moving issue. !3934 (Long Nguyen) - Assign labels and milestone to target project when moving issue. !3934 (Long Nguyen)
- Project#open_branches has been cleaned up and no longer loads entire records into memory.
- Log to application.log when an admin starts and stops impersonating a user
- Make build status canceled if any of the jobs was canceled and none failed
- Sanitize repo paths in new project error message
- Remove future dates from contribution calendar graph. - Remove future dates from contribution calendar graph.
- Support e-mail notifications for comments on project snippets
- Use ActionDispatch Remote IP for Akismet checking
- Fix error when visiting commit builds page before build was updated - Fix error when visiting commit builds page before build was updated
- Add 'l' shortcut to open Label dropdown on issuables and 'i' to create new issue on a project - Add 'l' shortcut to open Label dropdown on issuables and 'i' to create new issue on a project
- Updated search UI - Updated search UI
- Display informative message when new milestone is created
v 8.7.1 (unreleased) - Allow "NEWS" and "CHANGES" as alternative names for CHANGELOG. !3768 (Connor Shea)
- Added button to toggle whitespaces changes on diff view
- Backport GitLab Enterprise support from EE
- Create tags using Rugged for performance reasons. !3745
- Files over 5MB can only be viewed in their raw form, files over 1MB without highlighting !3718
- Add support for supressing text diffs using .gitattributes on the default branch (Matt Oakes)
- Added multiple colors for labels in dropdowns when dups happen.
- Improve description for the Two-factor Authentication sign-in screen. (Connor Shea)
- API support for the 'since' and 'until' operators on commit requests (Paco Guzman)
- Fix Gravatar hint in user profile when Gravatar is disabled. !3988 (Artem Sidorenko)
- Expire repository exists? and has_visible_content? caches after a push if necessary
- Merge request widget displays TeamCity build state and code coverage correctly again.
v 8.7.3
- Emails, Gitlab::Email::Message, Gitlab::Diff, and Premailer::Adapter::Nokogiri are now instrumented
v 8.7.2
- The "New Branch" button is now loaded asynchronously
- Fix error 500 when trying to create a wiki page
- Updated spacing between notification label and button
v 8.7.1
- Throttle the update of `project.last_activity_at` to 1 minute. !3848 - Throttle the update of `project.last_activity_at` to 1 minute. !3848
- Fix .gitlab-ci.yml parsing issue when hidde job is a template without script definition. !3849 - Fix .gitlab-ci.yml parsing issue when hidde job is a template without script definition. !3849
- Fix license detection to detect all license files, not only known licenses. !3878 - Fix license detection to detect all license files, not only known licenses. !3878
- Use the `can?` helper instead of `current_user.can?`. !3882 - Use the `can?` helper instead of `current_user.can?`. !3882
- Prevent users from deleting Webhooks via API they do not own - Prevent users from deleting Webhooks via API they do not own
- Fix Error 500 due to stale cache when projects are renamed or transferred - Fix Error 500 due to stale cache when projects are renamed or transferred
- Update width of search box to fix Safari bug. !3900 (Jedidiah)
- Use the `can?` helper instead of `current_user.can?`
v 8.7.0 v 8.7.0
- Gitlab::GitAccess and Gitlab::GitAccessWiki are now instrumented - Gitlab::GitAccess and Gitlab::GitAccessWiki are now instrumented
...@@ -125,13 +154,25 @@ v 8.7.0 ...@@ -125,13 +154,25 @@ v 8.7.0
- Import GitHub labels - Import GitHub labels
- Add option to filter by "Owned projects" on dashboard page - Add option to filter by "Owned projects" on dashboard page
- Import GitHub milestones - Import GitHub milestones
- Fix emoji catgories in the emoji picker
- Execute system web hooks on push to the project - Execute system web hooks on push to the project
- Allow enable/disable push events for system hooks - Allow enable/disable push events for system hooks
- Fix GitHub project's link in the import page when provider has a custom URL - Fix GitHub project's link in the import page when provider has a custom URL
- Add RAW build trace output and button on build page - Add RAW build trace output and button on build page
- Add incremental build trace update into CI API - Add incremental build trace update into CI API
v 8.6.8
- Prevent privilege escalation via "impersonate" feature
- Prevent privilege escalation via notes API
- Prevent privilege escalation via project webhook API
- Prevent XSS via Git branch and tag names
- Prevent XSS via custom issue tracker URL
- Prevent XSS via `window.opener`
- Prevent XSS via label drop-down
- Prevent information disclosure via milestone API
- Prevent information disclosure via snippet API
- Prevent information disclosure via project labels
- Prevent information disclosure via new merge request page
v 8.6.7 v 8.6.7
- Fix persistent XSS vulnerability in `commit_person_link` helper - Fix persistent XSS vulnerability in `commit_person_link` helper
- Fix persistent XSS vulnerability in Label and Milestone dropdowns - Fix persistent XSS vulnerability in Label and Milestone dropdowns
...@@ -273,6 +314,17 @@ v 8.6.0 ...@@ -273,6 +314,17 @@ v 8.6.0
- Trigger a todo for mentions on commits page - Trigger a todo for mentions on commits page
- Let project owners and admins soft delete issues and merge requests - Let project owners and admins soft delete issues and merge requests
v 8.5.12
- Prevent privilege escalation via "impersonate" feature
- Prevent privilege escalation via notes API
- Prevent privilege escalation via project webhook API
- Prevent XSS via Git branch and tag names
- Prevent XSS via custom issue tracker URL
- Prevent XSS via `window.opener`
- Prevent information disclosure via snippet API
- Prevent information disclosure via project labels
- Prevent information disclosure via new merge request page
v 8.5.11 v 8.5.11
- Fix persistent XSS vulnerability in `commit_person_link` helper - Fix persistent XSS vulnerability in `commit_person_link` helper
...@@ -423,6 +475,17 @@ v 8.5.0 ...@@ -423,6 +475,17 @@ v 8.5.0
- Show label row when filtering issues or merge requests by label (Nuttanart Pornprasitsakul) - Show label row when filtering issues or merge requests by label (Nuttanart Pornprasitsakul)
- Add Todos - Add Todos
v 8.4.10
- Prevent privilege escalation via "impersonate" feature
- Prevent privilege escalation via notes API
- Prevent privilege escalation via project webhook API
- Prevent XSS via Git branch and tag names
- Prevent XSS via custom issue tracker URL
- Prevent XSS via `window.opener`
- Prevent information disclosure via snippet API
- Prevent information disclosure via project labels
- Prevent information disclosure via new merge request page
v 8.4.9 v 8.4.9
- Fix persistent XSS vulnerability in `commit_person_link` helper - Fix persistent XSS vulnerability in `commit_person_link` helper
...@@ -548,6 +611,15 @@ v 8.4.0 ...@@ -548,6 +611,15 @@ v 8.4.0
- Add IP check against DNSBLs at account sign-up - Add IP check against DNSBLs at account sign-up
- Added cache:key to .gitlab-ci.yml allowing to fine tune the caching - Added cache:key to .gitlab-ci.yml allowing to fine tune the caching
v 8.3.9
- Prevent privilege escalation via "impersonate" feature
- Prevent privilege escalation via notes API
- Prevent privilege escalation via project webhook API
- Prevent XSS via custom issue tracker URL
- Prevent XSS via `window.opener`
- Prevent information disclosure via project labels
- Prevent information disclosure via new merge request page
v 8.3.8 v 8.3.8
- Fix persistent XSS vulnerability in `commit_person_link` helper - Fix persistent XSS vulnerability in `commit_person_link` helper
...@@ -657,6 +729,17 @@ v 8.3.0 ...@@ -657,6 +729,17 @@ v 8.3.0
- Expose Git's version in the admin area - Expose Git's version in the admin area
- Show "New Merge Request" buttons on canonical repos when you have a fork (Josh Frye) - Show "New Merge Request" buttons on canonical repos when you have a fork (Josh Frye)
v 8.2.5
- Prevent privilege escalation via "impersonate" feature
- Prevent privilege escalation via notes API
- Prevent privilege escalation via project webhook API
- Prevent XSS via `window.opener`
- Prevent information disclosure via project labels
- Prevent information disclosure via new merge request page
v 8.2.4
- Bump Git version requirement to 2.7.4
v 8.2.3 v 8.2.3
- Fix application settings cache not expiring after changes (Stan Hu) - Fix application settings cache not expiring after changes (Stan Hu)
- Fix Error 500s when creating global milestones with Unicode characters (Stan Hu) - Fix Error 500s when creating global milestones with Unicode characters (Stan Hu)
......
...@@ -38,7 +38,7 @@ source edition, and GitLab Enterprise Edition (EE) which is our commercial ...@@ -38,7 +38,7 @@ source edition, and GitLab Enterprise Edition (EE) which is our commercial
edition. Throughout this guide you will see references to CE and EE for edition. Throughout this guide you will see references to CE and EE for
abbreviation. abbreviation.
If you have read this guide and want to know how the GitLab [core team][core-team] If you have read this guide and want to know how the GitLab [core team]
operates please see [the GitLab contributing process](PROCESS.md). operates please see [the GitLab contributing process](PROCESS.md).
## Contributor license agreement ## Contributor license agreement
...@@ -135,12 +135,23 @@ For feature proposals for EE, open an issue on the ...@@ -135,12 +135,23 @@ For feature proposals for EE, open an issue on the
In order to help track the feature proposals, we have created a In order to help track the feature proposals, we have created a
[`feature proposal`][fpl] label. For the time being, users that are not members [`feature proposal`][fpl] label. For the time being, users that are not members
of the project cannot add labels. You can instead ask one of the [core team][core-team] of the project cannot add labels. You can instead ask one of the [core team]
members to add the label `feature proposal` to the issue. members to add the label `feature proposal` to the issue or add the following
code snippet right after your description in a new line: `~"feature proposal"`.
Please keep feature proposals as small and simple as possible, complex ones Please keep feature proposals as small and simple as possible, complex ones
might be edited to make them small and simple. might be edited to make them small and simple.
You are encouraged to use the template below for feature proposals.
```
## Description including problem, use cases, benefits, and/or goals
## Proposal
## Links / references
```
For changes in the interface, it can be helpful to create a mockup first. For changes in the interface, it can be helpful to create a mockup first.
If you want to create something yourself, consider opening an issue first to If you want to create something yourself, consider opening an issue first to
discuss whether it is interesting to include this in GitLab. discuss whether it is interesting to include this in GitLab.
...@@ -344,12 +355,11 @@ is it will be merged (quickly). After that you can send more MRs to enhance it. ...@@ -344,12 +355,11 @@ is it will be merged (quickly). After that you can send more MRs to enhance it.
For examples of feedback on merge requests please look at already For examples of feedback on merge requests please look at already
[closed merge requests][closed-merge-requests]. If you would like quick feedback [closed merge requests][closed-merge-requests]. If you would like quick feedback
on your merge request feel free to mention one of the Merge Marshalls in the on your merge request feel free to mention one of the Merge Marshalls in the
[core team][core-team] or one of the [core team] or one of the [Merge request coaches](https://about.gitlab.com/team/).
[Merge request coaches](https://about.gitlab.com/team/).
Please ensure that your merge request meets the contribution acceptance criteria. Please ensure that your merge request meets the contribution acceptance criteria.
When having your code reviewed and when reviewing merge requests please take the When having your code reviewed and when reviewing merge requests please take the
[Thoughtbot code review guide] into account. [code review guidelines](doc/development/code_review.md) into account.
### Merge request description format ### Merge request description format
...@@ -497,7 +507,7 @@ reported by emailing `contact@gitlab.com`. ...@@ -497,7 +507,7 @@ reported by emailing `contact@gitlab.com`.
This Code of Conduct is adapted from the [Contributor Covenant][contributor-covenant], version 1.1.0, This Code of Conduct is adapted from the [Contributor Covenant][contributor-covenant], version 1.1.0,
available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/). available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/).
[core-team]: https://about.gitlab.com/core-team/ [core team]: https://about.gitlab.com/core-team/
[getting-help]: https://about.gitlab.com/getting-help/ [getting-help]: https://about.gitlab.com/getting-help/
[codetriage]: http://www.codetriage.com/gitlabhq/gitlabhq [codetriage]: http://www.codetriage.com/gitlabhq/gitlabhq
[up-for-grabs]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=up-for-grabs [up-for-grabs]: https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=up-for-grabs
...@@ -523,4 +533,3 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor ...@@ -523,4 +533,3 @@ available at [http://contributor-covenant.org/version/1/1/0/](http://contributor
[gitlab-design]: https://gitlab.com/gitlab-org/gitlab-design [gitlab-design]: https://gitlab.com/gitlab-org/gitlab-design
[free Antetype viewer (Mac OSX only)]: https://itunes.apple.com/us/app/antetype-viewer/id824152298?mt=12 [free Antetype viewer (Mac OSX only)]: https://itunes.apple.com/us/app/antetype-viewer/id824152298?mt=12
[`gitlab1.atype` file]: https://gitlab.com/gitlab-org/gitlab-design/tree/master/gitlab1.atype/ [`gitlab1.atype` file]: https://gitlab.com/gitlab-org/gitlab-design/tree/master/gitlab1.atype/
[Thoughtbot code review guide]: https://github.com/thoughtbot/guides/tree/master/code-review
...@@ -19,8 +19,8 @@ gem "pg", '~> 0.18.2', group: :postgres ...@@ -19,8 +19,8 @@ gem "pg", '~> 0.18.2', group: :postgres
# Authentication libraries # Authentication libraries
gem 'devise', '~> 3.5.4' gem 'devise', '~> 3.5.4'
gem 'doorkeeper', '~> 3.1'
gem 'devise-async', '~> 0.9.0' gem 'devise-async', '~> 0.9.0'
gem 'doorkeeper', '~> 2.2.0'
gem 'omniauth', '~> 1.3.1' gem 'omniauth', '~> 1.3.1'
gem 'omniauth-auth0', '~> 1.4.1' gem 'omniauth-auth0', '~> 1.4.1'
gem 'omniauth-azure-oauth2', '~> 0.0.6' gem 'omniauth-azure-oauth2', '~> 0.0.6'
...@@ -243,7 +243,7 @@ group :development do ...@@ -243,7 +243,7 @@ group :development do
gem 'brakeman', '~> 3.2.0', require: false gem 'brakeman', '~> 3.2.0', require: false
gem "annotate", "~> 2.7.0" gem "annotate", "~> 2.7.0"
gem "letter_opener", '~> 1.1.2' gem 'letter_opener_web', '~> 1.3.0'
gem 'quiet_assets', '~> 1.0.2' gem 'quiet_assets', '~> 1.0.2'
gem 'rerun', '~> 0.11.0' gem 'rerun', '~> 0.11.0'
gem 'bullet', require: false gem 'bullet', require: false
...@@ -270,7 +270,7 @@ group :development, :test do ...@@ -270,7 +270,7 @@ group :development, :test do
gem 'database_cleaner', '~> 1.4.0' gem 'database_cleaner', '~> 1.4.0'
gem 'factory_girl_rails', '~> 4.6.0' gem 'factory_girl_rails', '~> 4.6.0'
gem 'rspec-rails', '~> 3.3.0' gem 'rspec-rails', '~> 3.4.0'
gem 'rspec-retry' gem 'rspec-retry'
gem 'spinach-rails', '~> 0.2.1' gem 'spinach-rails', '~> 0.2.1'
gem 'spinach-rerun-reporter', '~> 0.0.2' gem 'spinach-rerun-reporter', '~> 0.0.2'
......
...@@ -175,7 +175,7 @@ GEM ...@@ -175,7 +175,7 @@ GEM
diff-lcs (1.2.5) diff-lcs (1.2.5)
diffy (3.0.7) diffy (3.0.7)
docile (1.1.5) docile (1.1.5)
doorkeeper (2.2.2) doorkeeper (3.1.0)
railties (>= 3.2) railties (>= 3.2)
dropzonejs-rails (0.7.2) dropzonejs-rails (0.7.2)
rails (> 3.1) rails (> 3.1)
...@@ -186,7 +186,7 @@ GEM ...@@ -186,7 +186,7 @@ GEM
encryptor (1.3.0) encryptor (1.3.0)
equalizer (0.0.11) equalizer (0.0.11)
erubis (2.7.0) erubis (2.7.0)
escape_utils (1.1.0) escape_utils (1.1.1)
eventmachine (1.0.8) eventmachine (1.0.8)
excon (0.45.4) excon (0.45.4)
execjs (2.6.0) execjs (2.6.0)
...@@ -336,7 +336,7 @@ GEM ...@@ -336,7 +336,7 @@ GEM
json json
get_process_mem (0.2.0) get_process_mem (0.2.0)
gherkin-ruby (0.3.2) gherkin-ruby (0.3.2)
github-linguist (4.7.5) github-linguist (4.7.6)
charlock_holmes (~> 0.7.3) charlock_holmes (~> 0.7.3)
escape_utils (~> 1.1.0) escape_utils (~> 1.1.0)
mime-types (>= 1.19) mime-types (>= 1.19)
...@@ -353,7 +353,7 @@ GEM ...@@ -353,7 +353,7 @@ GEM
posix-spawn (~> 0.3) posix-spawn (~> 0.3)
gitlab_emoji (0.3.1) gitlab_emoji (0.3.1)
gemojione (~> 2.2, >= 2.2.1) gemojione (~> 2.2, >= 2.2.1)
gitlab_git (10.0.0) gitlab_git (10.0.2)
activesupport (~> 4.0) activesupport (~> 4.0)
charlock_holmes (~> 0.7.3) charlock_holmes (~> 0.7.3)
github-linguist (~> 4.7.0) github-linguist (~> 4.7.0)
...@@ -450,8 +450,12 @@ GEM ...@@ -450,8 +450,12 @@ GEM
kgio (2.10.0) kgio (2.10.0)
launchy (2.4.3) launchy (2.4.3)
addressable (~> 2.3) addressable (~> 2.3)
letter_opener (1.1.2) letter_opener (1.4.1)
launchy (~> 2.2) launchy (~> 2.2)
letter_opener_web (1.3.0)
actionmailer (>= 3.2)
letter_opener (~> 1.0)
railties (>= 3.2)
licensee (8.0.0) licensee (8.0.0)
rugged (>= 0.24b) rugged (>= 0.24b)
listen (3.0.5) listen (3.0.5)
...@@ -660,29 +664,29 @@ GEM ...@@ -660,29 +664,29 @@ GEM
chunky_png chunky_png
rqrcode-rails3 (0.1.7) rqrcode-rails3 (0.1.7)
rqrcode (>= 0.4.2) rqrcode (>= 0.4.2)
rspec (3.3.0) rspec (3.4.0)
rspec-core (~> 3.3.0) rspec-core (~> 3.4.0)
rspec-expectations (~> 3.3.0) rspec-expectations (~> 3.4.0)
rspec-mocks (~> 3.3.0) rspec-mocks (~> 3.4.0)
rspec-core (3.3.2) rspec-core (3.4.4)
rspec-support (~> 3.3.0) rspec-support (~> 3.4.0)
rspec-expectations (3.3.1) rspec-expectations (3.4.0)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.3.0) rspec-support (~> 3.4.0)
rspec-mocks (3.3.2) rspec-mocks (3.4.1)
diff-lcs (>= 1.2.0, < 2.0) diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.3.0) rspec-support (~> 3.4.0)
rspec-rails (3.3.3) rspec-rails (3.4.2)
actionpack (>= 3.0, < 4.3) actionpack (>= 3.0, < 4.3)
activesupport (>= 3.0, < 4.3) activesupport (>= 3.0, < 4.3)
railties (>= 3.0, < 4.3) railties (>= 3.0, < 4.3)
rspec-core (~> 3.3.0) rspec-core (~> 3.4.0)
rspec-expectations (~> 3.3.0) rspec-expectations (~> 3.4.0)
rspec-mocks (~> 3.3.0) rspec-mocks (~> 3.4.0)
rspec-support (~> 3.3.0) rspec-support (~> 3.4.0)
rspec-retry (0.4.5) rspec-retry (0.4.5)
rspec-core rspec-core
rspec-support (3.3.0) rspec-support (3.4.1)
rubocop (0.38.0) rubocop (0.38.0)
parser (>= 2.3.0.6, < 3.0) parser (>= 2.3.0.6, < 3.0)
powerpack (~> 0.1) powerpack (~> 0.1)
...@@ -921,7 +925,7 @@ DEPENDENCIES ...@@ -921,7 +925,7 @@ DEPENDENCIES
devise-async (~> 0.9.0) devise-async (~> 0.9.0)
devise-two-factor (~> 2.0.0) devise-two-factor (~> 2.0.0)
diffy (~> 3.0.3) diffy (~> 3.0.3)
doorkeeper (~> 2.2.0) doorkeeper (~> 3.1)
dropzonejs-rails (~> 0.7.1) dropzonejs-rails (~> 0.7.1)
email_reply_parser (~> 0.5.8) email_reply_parser (~> 0.5.8)
email_spec (~> 1.6.0) email_spec (~> 1.6.0)
...@@ -957,7 +961,7 @@ DEPENDENCIES ...@@ -957,7 +961,7 @@ DEPENDENCIES
jquery-turbolinks (~> 2.1.0) jquery-turbolinks (~> 2.1.0)
jquery-ui-rails (~> 5.0.0) jquery-ui-rails (~> 5.0.0)
kaminari (~> 0.16.3) kaminari (~> 0.16.3)
letter_opener (~> 1.1.2) letter_opener_web (~> 1.3.0)
licensee (~> 8.0.0) licensee (~> 8.0.0)
loofah (~> 2.0.3) loofah (~> 2.0.3)
mail_room (~> 0.6.1) mail_room (~> 0.6.1)
...@@ -1010,7 +1014,7 @@ DEPENDENCIES ...@@ -1010,7 +1014,7 @@ DEPENDENCIES
responders (~> 2.0) responders (~> 2.0)
rouge (~> 1.10.1) rouge (~> 1.10.1)
rqrcode-rails3 (~> 0.1.7) rqrcode-rails3 (~> 0.1.7)
rspec-rails (~> 3.3.0) rspec-rails (~> 3.4.0)
rspec-retry rspec-retry
rubocop (~> 0.38.0) rubocop (~> 0.38.0)
ruby-fogbugz (~> 0.2.1) ruby-fogbugz (~> 0.2.1)
...@@ -1057,4 +1061,4 @@ DEPENDENCIES ...@@ -1057,4 +1061,4 @@ DEPENDENCIES
wikicloth (= 0.8.1) wikicloth (= 0.8.1)
BUNDLED WITH BUNDLED WITH
1.11.2 1.12.1
# GitLab # GitLab
[![build status](https://ci.gitlab.com/projects/1/status.svg?ref=master)](https://ci.gitlab.com/projects/1?ref=master) [![build status](https://gitlab.com/gitlab-org/gitlab-ce/badges/master/build.svg)](https://gitlab.com/gitlab-org/gitlab-ce/commits/master)
[![Build Status](https://semaphoreci.com/api/v1/projects/2f1a5809-418b-4cc2-a1f4-819607579fe7/400484/shields_badge.svg)](https://semaphoreci.com/gitlabhq/gitlabhq) [![Build Status](https://semaphoreci.com/api/v1/projects/2f1a5809-418b-4cc2-a1f4-819607579fe7/400484/shields_badge.svg)](https://semaphoreci.com/gitlabhq/gitlabhq)
[![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq) [![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.svg)](https://codeclimate.com/github/gitlabhq/gitlabhq)
[![Coverage Status](https://coveralls.io/repos/gitlabhq/gitlabhq/badge.svg?branch=master)](https://coveralls.io/r/gitlabhq/gitlabhq?branch=master) [![Coverage Status](https://coveralls.io/repos/gitlabhq/gitlabhq/badge.svg?branch=master)](https://coveralls.io/r/gitlabhq/gitlabhq?branch=master)
...@@ -80,7 +80,7 @@ There are a lot of [third-party applications integrating with GitLab](https://ab ...@@ -80,7 +80,7 @@ There are a lot of [third-party applications integrating with GitLab](https://ab
## GitLab release cycle ## GitLab release cycle
For more information about the release process see the [release documentation](http://doc.gitlab.com/ce/release/). For more information about the release process see the [release documentation](https://gitlab.com/gitlab-org/release-tools/blob/master/README.md).
## Upgrading ## Upgrading
......
...@@ -184,6 +184,9 @@ class GitLabDropdown ...@@ -184,6 +184,9 @@ class GitLabDropdown
@dropdown.on "shown.bs.dropdown", @opened @dropdown.on "shown.bs.dropdown", @opened
@dropdown.on "hidden.bs.dropdown", @hidden @dropdown.on "hidden.bs.dropdown", @hidden
@dropdown.on "click", ".dropdown-menu, .dropdown-menu-close", @shouldPropagate @dropdown.on "click", ".dropdown-menu, .dropdown-menu-close", @shouldPropagate
@dropdown.on 'keyup', (e) =>
if e.which is 27 # Escape key
$('.dropdown-menu-close', @dropdown).trigger 'click'
if @dropdown.find(".dropdown-toggle-page").length if @dropdown.find(".dropdown-toggle-page").length
@dropdown.find(".dropdown-toggle-page, .dropdown-menu-back").on "click", (e) => @dropdown.find(".dropdown-toggle-page, .dropdown-menu-back").on "click", (e) =>
......
...@@ -12,6 +12,7 @@ class @Issue ...@@ -12,6 +12,7 @@ class @Issue
@initMergeRequests() @initMergeRequests()
@initRelatedBranches() @initRelatedBranches()
@initCanCreateBranch()
initTaskList: -> initTaskList: ->
$('.detail-page-description .js-task-list-container').taskList('enable') $('.detail-page-description .js-task-list-container').taskList('enable')
...@@ -92,3 +93,25 @@ class @Issue ...@@ -92,3 +93,25 @@ class @Issue
.success (data) -> .success (data) ->
if 'html' of data if 'html' of data
$container.html(data.html) $container.html(data.html)
initCanCreateBranch: ->
$container = $('div#new-branch')
# If the user doesn't have the required permissions the container isn't
# rendered at all.
return unless $container
$.getJSON($container.data('path'))
.error ->
$container.find('.checking').hide()
$container.find('.unavailable').show()
new Flash('Failed to check if a new branch can be created.', 'alert')
.success (data) ->
if data.can_create_branch
$container.find('.checking').hide()
$container.find('.available').show()
$container.find('a').attr('disabled', false)
else
$container.find('.checking').hide()
$container.find('.unavailable').show()
...@@ -30,7 +30,7 @@ class @LabelsSelect ...@@ -30,7 +30,7 @@ class @LabelsSelect
if issueUpdateURL if issueUpdateURL
labelHTMLTemplate = _.template( labelHTMLTemplate = _.template(
'<% _.each(labels, function(label){ %> '<% _.each(labels, function(label){ %>
<a href="<%= ["",issueURLSplit[1], issueURLSplit[2],""].join("/") %>issues?label_name=<%= _.escape(label.title) %>"> <a href="<%= ["",issueURLSplit[1], issueURLSplit[2],""].join("/") %>issues?label_name[]=<%= _.escape(label.title) %>">
<span class="label has-tooltip color-label" title="<%= _.escape(label.description) %>" style="background-color: <%= label.color %>; color: <%= label.text_color %>;"> <span class="label has-tooltip color-label" title="<%= _.escape(label.description) %>" style="background-color: <%= label.color %>; color: <%= label.text_color %>;">
<%= _.escape(label.title) %> <%= _.escape(label.title) %>
</span> </span>
...@@ -163,6 +163,21 @@ class @LabelsSelect ...@@ -163,6 +163,21 @@ class @LabelsSelect
$.ajax( $.ajax(
url: labelUrl url: labelUrl
).done (data) -> ).done (data) ->
data = _.chain data
.groupBy (label) ->
label.title
.map (label) ->
color = _.map label, (dup) ->
dup.color
return {
id: label[0].id
title: label[0].title
color: color
duplicate: color.length > 1
}
.value()
if $dropdown.hasClass 'js-extra-options' if $dropdown.hasClass 'js-extra-options'
if showNo if showNo
data.unshift( data.unshift(
...@@ -178,6 +193,7 @@ class @LabelsSelect ...@@ -178,6 +193,7 @@ class @LabelsSelect
if data.length > 2 if data.length > 2
data.splice 2, 0, 'divider' data.splice 2, 0, 'divider'
callback data callback data
renderRow: (label) -> renderRow: (label) ->
...@@ -192,11 +208,31 @@ class @LabelsSelect ...@@ -192,11 +208,31 @@ class @LabelsSelect
if $dropdown.hasClass('js-multiselect') and removesAll if $dropdown.hasClass('js-multiselect') and removesAll
selectedClass.push 'dropdown-clear-active' selectedClass.push 'dropdown-clear-active'
color = if label.color? then "<span class='dropdown-label-box' style='background-color: #{label.color}'></span>" else "" if label.duplicate
spacing = 100 / label.color.length
# Reduce the colors to 4
label.color = label.color.filter (color, i) ->
i < 4
color = _.map(label.color, (color, i) ->
percentFirst = Math.floor(spacing * i)
percentSecond = Math.floor(spacing * (i + 1))
"#{color} #{percentFirst}%,#{color} #{percentSecond}% "
).join(',')
color = "linear-gradient(#{color})"
else
if label.color?
color = label.color[0]
if color
colorEl = "<span class='dropdown-label-box' style='background: #{color}'></span>"
else
colorEl = ''
"<li> "<li>
<a href='#' class='#{selectedClass.join(' ')}'> <a href='#' class='#{selectedClass.join(' ')}'>
#{color} #{colorEl}
#{_.escape(label.title)} #{_.escape(label.title)}
</a> </a>
</li>" </li>"
......
...@@ -68,20 +68,18 @@ class @MergeRequestWidget ...@@ -68,20 +68,18 @@ class @MergeRequestWidget
$.getJSON @opts.ci_status_url, (data) => $.getJSON @opts.ci_status_url, (data) =>
@readyForCICheck = true @readyForCICheck = true
if @firstCICheck if data.status is ''
@firstCICheck = false
@opts.ci_status = data.status
if @opts.ci_status is ''
@opts.ci_status = data.status
return return
if data.status isnt @opts.ci_status and data.status? if @firstCiCheck || data.status isnt @opts.ci_status and data.status?
@opts.ci_status = data.status
@showCIStatus data.status @showCIStatus data.status
if data.coverage if data.coverage
@showCICoverage data.coverage @showCICoverage data.coverage
if showNotification # The first check should only update the UI, a notification
# should only be displayed on status changes
if showNotification and not @firstCiCheck
status = @ciLabelForStatus(data.status) status = @ciLabelForStatus(data.status)
if status is "preparing" if status is "preparing"
...@@ -104,8 +102,7 @@ class @MergeRequestWidget ...@@ -104,8 +102,7 @@ class @MergeRequestWidget
@close() @close()
Turbolinks.visit _this.opts.builds_path Turbolinks.visit _this.opts.builds_path
) )
@firstCiCheck = false
@opts.ci_status = data.status
showCIStatus: (state) -> showCIStatus: (state) ->
$('.ci_widget').hide() $('.ci_widget').hide()
......
...@@ -167,8 +167,8 @@ class @Notes ...@@ -167,8 +167,8 @@ class @Notes
return return
if note.award if note.award
awards_handler.addAwardToEmojiBar(note.note) awardsHandler.addAwardToEmojiBar(note.note)
awards_handler.scrollToAwards() awardsHandler.scrollToAwards()
# render note if it not present in loaded list # render note if it not present in loaded list
# or skip if rendered # or skip if rendered
...@@ -373,11 +373,11 @@ class @Notes ...@@ -373,11 +373,11 @@ class @Notes
new GLForm form new GLForm form
if scrollTo? and myLastNote? if scrollTo? and myLastNote?
# scroll to the bottom # scroll to the bottom
# so the open of the last element doesn't make a jump # so the open of the last element doesn't make a jump
$('html, body').scrollTop($(document).height()); $('html, body').scrollTop($(document).height());
$('html, body').animate({ $('html, body').animate({
scrollTop: myLastNote.offset().top - 150 scrollTop: myLastNote.offset().top - 150
}, 500, -> }, 500, ->
$noteText = form.find(".js-note-text") $noteText = form.find(".js-note-text")
$noteText.focus() $noteText.focus()
......
...@@ -92,7 +92,7 @@ class @UserTabs ...@@ -92,7 +92,7 @@ class @UserTabs
@setCurrentAction(action) @setCurrentAction(action)
activateTab: (action) -> activateTab: (action) ->
@parentEl.find(".nav-links .#{action}-tab a").tab('show') @parentEl.find(".nav-links .js-#{action}-tab a").tab('show')
setTab: (source, action) -> setTab: (source, action) ->
return if @loaded[action] is true return if @loaded[action] is true
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
@import "framework/lists.scss"; @import "framework/lists.scss";
@import "framework/markdown_area.scss"; @import "framework/markdown_area.scss";
@import "framework/mobile.scss"; @import "framework/mobile.scss";
@import "framework/modal.scss";
@import "framework/nav.scss"; @import "framework/nav.scss";
@import "framework/pagination.scss"; @import "framework/pagination.scss";
@import "framework/progress.scss"; @import "framework/progress.scss";
......
.light-well { .light-well {
background-color: #f8fafc; background-color: $background-color;
padding: 15px; padding: 15px;
} }
......
...@@ -139,6 +139,10 @@ ...@@ -139,6 +139,10 @@
pointer-events: auto !important; pointer-events: auto !important;
} }
&[disabled] {
pointer-events: none !important;
}
.caret { .caret {
margin-left: 5px; margin-left: 5px;
} }
......
...@@ -78,6 +78,24 @@ label { ...@@ -78,6 +78,24 @@ label {
border-radius: 3px; border-radius: 3px;
} }
.select-wrapper {
position: relative;
.caret {
position: absolute;
right: 10px;
top: $gl-padding;
color: $gray-darkest;
pointer-events: none;
}
}
.select-control {
padding-left: 10px;
padding-right: 10px;
-webkit-appearance: none;
}
.form-control-inline { .form-control-inline {
display: inline; display: inline;
} }
......
...@@ -26,9 +26,9 @@ header { ...@@ -26,9 +26,9 @@ header {
z-index: 100; z-index: 100;
margin-bottom: 0; margin-bottom: 0;
min-height: $header-height; min-height: $header-height;
background-color: #fff; background-color: $background-color;
border: none; border: none;
border-bottom: 1px solid #eee; border-bottom: 1px solid $border-color;
.container-fluid { .container-fluid {
width: 100% !important; width: 100% !important;
...@@ -47,7 +47,7 @@ header { ...@@ -47,7 +47,7 @@ header {
text-align: center; text-align: center;
&:hover, &:focus, &:active { &:hover, &:focus, &:active {
background-color: #fff; background-color: $background-color;
} }
} }
......
.modal-body {
position: relative;
overflow-y: auto;
padding: 15px;
.form-actions {
margin: -$gl-padding+1;
margin-top: 15px;
}
.text-danger {
font-weight: bold;
}
}
body.modal-open {
overflow: hidden;
}
.modal .modal-dialog {
width: 860px;
}
...@@ -185,3 +185,22 @@ ...@@ -185,3 +185,22 @@
} }
} }
} }
.layout-nav {
background: $background-color;
border-bottom: 1px solid $border-color;
.controls {
float: right;
position: relative;
top: 10px;
.dropdown {
margin-left: 7px;
}
}
.nav-links {
border-bottom: none;
}
}
...@@ -7,13 +7,11 @@ ...@@ -7,13 +7,11 @@
.select2-choice { .select2-choice {
background: #fff; background: #fff;
border-color: $input-border; border-color: $input-border;
border-color: $border-white-light;
height: 35px; height: 35px;
padding: $gl-vert-padding $gl-btn-padding; padding: $gl-vert-padding $gl-btn-padding;
font-size: $gl-font-size; font-size: $gl-font-size;
line-height: 1.42857143; line-height: 1.42857143;
border-radius: $border-radius-base;
@include border-radius($border-radius-default);
.select2-arrow { .select2-arrow {
background-image: none; background-image: none;
...@@ -199,6 +197,14 @@ ...@@ -199,6 +197,14 @@
} }
} }
.select2-highlighted {
.group-result {
.group-path {
color: #fff;
}
}
}
.group-result { .group-result {
.group-image { .group-image {
float: left; float: left;
......
...@@ -32,13 +32,11 @@ table { ...@@ -32,13 +32,11 @@ table {
th { th {
background-color: $background-color; background-color: $background-color;
font-weight: normal; font-weight: normal;
font-size: 15px; border-bottom: none;
border-bottom: 1px solid $border-color;
} }
td { td {
border-color: $table-border-color; border-color: $table-border-color;
border-bottom: 1px solid $border-color;
} }
} }
} }
......
...@@ -153,8 +153,8 @@ $nav-link-padding: 13px $gl-padding; ...@@ -153,8 +153,8 @@ $nav-link-padding: 13px $gl-padding;
//== Code //== Code
// //
//## //##
$pre-bg: #f8fafc !default; $pre-bg: $background-color !default;
$pre-color: $gl-gray !default; $pre-color: $gl-gray !default;
$pre-border-color: #e7e9ed; $pre-border-color: $border-color;
$table-bg-accent: $background-color; $table-bg-accent: $background-color;
...@@ -205,6 +205,10 @@ h1, h2, h3, h4, h5, h6 { ...@@ -205,6 +205,10 @@ h1, h2, h3, h4, h5, h6 {
font-weight: 600; font-weight: 600;
} }
.light-header {
font-weight: 600;
}
/** CODE **/ /** CODE **/
pre { pre {
font-family: $monospace_font; font-family: $monospace_font;
...@@ -259,3 +263,9 @@ h1, h2, h3, h4 { ...@@ -259,3 +263,9 @@ h1, h2, h3, h4 {
color: $gl-gray; color: $gl-gray;
} }
} }
.text-right-lg {
@media (min-width: $screen-lg-min) {
text-align: right;
}
}
...@@ -12,7 +12,7 @@ $gutter_inner_width: 258px; ...@@ -12,7 +12,7 @@ $gutter_inner_width: 258px;
*/ */
$border-color: #e5e5e5; $border-color: #e5e5e5;
$focus-border-color: #3aabf0; $focus-border-color: #3aabf0;
$table-border-color: #eef0f2; $table-border-color: #ececec;
$background-color: #fafafa; $background-color: #fafafa;
/* /*
...@@ -71,8 +71,7 @@ $gl-avatar-size: 40px; ...@@ -71,8 +71,7 @@ $gl-avatar-size: 40px;
$error-exclamation-point: #e62958; $error-exclamation-point: #e62958;
$border-radius-default: 2px; $border-radius-default: 2px;
$btn-transparent-color: #8f8f8f; $btn-transparent-color: #8f8f8f;
$ssh-key-icon-color: #8f8f8f; $settings-icon-size: 18px;
$ssh-key-icon-size: 18px;
$provider-btn-group-border: #e5e5e5; $provider-btn-group-border: #e5e5e5;
$provider-btn-not-active-color: #4688f1; $provider-btn-not-active-color: #4688f1;
......
...@@ -98,7 +98,11 @@ ...@@ -98,7 +98,11 @@
} }
td.line_content.parallel { td.line_content.parallel {
width: 50%; width: 46%;
}
.add-diff-note {
margin-left: -65px;
} }
} }
...@@ -127,8 +131,13 @@ ...@@ -127,8 +131,13 @@
margin: 0; margin: 0;
padding: 0 0.5em; padding: 0 0.5em;
border: none; border: none;
&.parallel { &.parallel {
display: table-cell; display: table-cell;
span {
word-break: break-all;
}
} }
} }
......
...@@ -55,25 +55,6 @@ ...@@ -55,25 +55,6 @@
} }
} }
.modal-body {
position: relative;
overflow-y: auto;
padding: 15px;
.form-actions {
margin: -$gl-padding+1;
margin-top: 15px;
}
}
body.modal-open {
overflow: hidden;
}
.modal .modal-dialog {
width: 860px;
}
.documentation { .documentation {
padding: 7px; padding: 7px;
} }
...@@ -28,7 +28,7 @@ li.milestone { ...@@ -28,7 +28,7 @@ li.milestone {
// Issue title // Issue title
span a { span a {
color: rgba(0,0,0,0.64); color: $gl-text-color;
} }
} }
} }
...@@ -51,7 +51,7 @@ li.milestone { ...@@ -51,7 +51,7 @@ li.milestone {
margin-top: 7px; margin-top: 7px;
.issuable-number { .issuable-number {
color: rgba(0,0,0,0.44); color: $gl-placeholder-color;
margin-right: 5px; margin-right: 5px;
} }
.avatar { .avatar {
......
...@@ -114,10 +114,6 @@ ul.notes { ...@@ -114,10 +114,6 @@ ul.notes {
word-break: keep-all; word-break: keep-all;
} }
} }
a {
word-break: break-all;
}
} }
.note-header { .note-header {
...@@ -215,7 +211,7 @@ ul.notes { ...@@ -215,7 +211,7 @@ ul.notes {
} }
.discussion-actions { .discussion-actions {
@media (max-width: $screen-sm-max) { @media (max-width: $screen-md-max) {
float: none; float: none;
margin-left: 0; margin-left: 0;
......
...@@ -18,7 +18,8 @@ ...@@ -18,7 +18,8 @@
} }
.account-btn-link, .account-btn-link,
.profile-settings-sidebar a { .profile-settings-sidebar a,
.settings-sidebar a {
color: $md-link-color; color: $md-link-color;
} }
...@@ -123,12 +124,6 @@ ...@@ -123,12 +124,6 @@
} }
} }
.key-icon {
color: $ssh-key-icon-color;
font-size: $ssh-key-icon-size;
line-height: 42px;
}
.key-created-at { .key-created-at {
line-height: 42px; line-height: 42px;
} }
...@@ -180,14 +175,6 @@ ...@@ -180,14 +175,6 @@
} }
} }
.profile-settings-message {
line-height: 32px;
color: $warning-message-color;
background-color: $warning-message-bg;
border: 1px solid $warning-message-border;
border-radius: $border-radius-base;
}
.oauth-applications { .oauth-applications {
form { form {
display: inline-block; display: inline-block;
......
...@@ -202,8 +202,31 @@ ...@@ -202,8 +202,31 @@
min-width: 200px; min-width: 200px;
} }
.deploy-project-label { .deploy-key-content {
margin: 1px; @media (min-width: $screen-sm-min) {
float: left;
&:last-child {
float: right;
}
}
}
.deploy-key-projects {
@media (min-width: $screen-sm-min) {
line-height: 42px;
}
}
a.deploy-project-label {
padding: 5px;
margin-right: 5px;
color: $gl-gray;
background-color: $row-hover;
&:hover {
color: $gl-link-color;
}
} }
.vs-public { .vs-public {
...@@ -256,12 +279,6 @@ ...@@ -256,12 +279,6 @@
} }
} }
table.table.protected-branches-list tr.no-border {
th, td {
border: 0;
}
}
.project-import .btn { .project-import .btn {
float: left; float: left;
margin-right: 10px; margin-right: 10px;
...@@ -474,3 +491,14 @@ pre.light-well { ...@@ -474,3 +491,14 @@ pre.light-well {
color: #fff; color: #fff;
} }
} }
.protected-branches-list {
a {
color: $gl-gray;
font-weight: 600;
&:hover {
color: $gl-link-color;
}
}
}
.settings-list-icon {
color: $gl-placeholder-color;
font-size: $settings-icon-size;
line-height: 42px;
}
.settings-message {
padding: 5px;
line-height: 1.3;
color: $warning-message-color;
background-color: $warning-message-bg;
border: 1px solid $warning-message-border;
border-radius: $border-radius-base;
}
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
tr { tr {
> td, > th { > td, > th {
line-height: 26px; line-height: 23px;
} }
&:hover { &:hover {
......
...@@ -6,12 +6,6 @@ class Admin::ApplicationController < ApplicationController ...@@ -6,12 +6,6 @@ class Admin::ApplicationController < ApplicationController
layout 'admin' layout 'admin'
def authenticate_admin! def authenticate_admin!
return render_404 unless current_user.is_admin? render_404 unless current_user.is_admin?
end
def authorize_impersonator!
if session[:impersonator_id]
User.find_by!(username: session[:impersonator_id]).admin?
end
end end
end end
...@@ -39,6 +39,12 @@ class Admin::HooksController < Admin::ApplicationController ...@@ -39,6 +39,12 @@ class Admin::HooksController < Admin::ApplicationController
end end
def hook_params def hook_params
params.require(:hook).permit(:url, :enable_ssl_verification, :push_events, :tag_push_events) params.require(:hook).permit(
:enable_ssl_verification,
:push_events,
:tag_push_events,
:token,
:url
)
end end
end end
class Admin::ImpersonationController < Admin::ApplicationController
skip_before_action :authenticate_admin!, only: :destroy
before_action :user
before_action :authorize_impersonator!
def create
if @user.blocked?
flash[:alert] = "You cannot impersonate a blocked user"
redirect_to admin_user_path(@user)
else
session[:impersonator_id] = current_user.username
session[:impersonator_return_to] = admin_user_path(@user)
warden.set_user(user, scope: 'user')
flash[:alert] = "You are impersonating #{user.username}."
redirect_to root_path
end
end
def destroy
redirect = session[:impersonator_return_to]
warden.set_user(user, scope: 'user')
session[:impersonator_return_to] = nil
session[:impersonator_id] = nil
redirect_to redirect || root_path
end
def user
@user ||= User.find_by!(username: params[:id] || session[:impersonator_id])
end
end
class Admin::ImpersonationsController < Admin::ApplicationController
skip_before_action :authenticate_admin!
before_action :authenticate_impersonator!
def destroy
original_user = current_user
warden.set_user(impersonator, scope: :user)
Gitlab::AppLogger.info("User #{original_user.username} has stopped impersonating #{impersonator.username}")
session[:impersonator_id] = nil
redirect_to admin_user_path(original_user)
end
private
def impersonator
@impersonator ||= User.find(session[:impersonator_id]) if session[:impersonator_id]
end
def authenticate_impersonator!
render_404 unless impersonator && impersonator.is_admin? && !impersonator.blocked?
end
end
...@@ -31,6 +31,24 @@ class Admin::UsersController < Admin::ApplicationController ...@@ -31,6 +31,24 @@ class Admin::UsersController < Admin::ApplicationController
user user
end end
def impersonate
if user.blocked?
flash[:alert] = "You cannot impersonate a blocked user"
redirect_to admin_user_path(user)
else
session[:impersonator_id] = current_user.id
warden.set_user(user, scope: :user)
Gitlab::AppLogger.info("User #{current_user.username} has started impersonating #{user.username}")
flash[:alert] = "You are now impersonating #{user.username}"
redirect_to root_path
end
end
def block def block
if user.block if user.block
redirect_back_or_admin_user(notice: "Successfully blocked") redirect_back_or_admin_user(notice: "Successfully blocked")
......
...@@ -117,7 +117,7 @@ class ApplicationController < ActionController::Base ...@@ -117,7 +117,7 @@ class ApplicationController < ActionController::Base
end end
def after_sign_out_path_for(resource) def after_sign_out_path_for(resource)
current_application_settings.after_sign_out_path || new_user_session_path current_application_settings.after_sign_out_path.presence || new_user_session_path
end end
def abilities def abilities
......
...@@ -15,7 +15,7 @@ class Projects::CommitsController < Projects::ApplicationController ...@@ -15,7 +15,7 @@ class Projects::CommitsController < Projects::ApplicationController
if search.present? if search.present?
@repository.find_commits_by_message(search, @ref, @path, @limit, @offset).compact @repository.find_commits_by_message(search, @ref, @path, @limit, @offset).compact
else else
@repository.commits(@ref, @path, @limit, @offset) @repository.commits(@ref, path: @path, limit: @limit, offset: @offset)
end end
@note_counts = project.notes.where(commit_id: @commits.map(&:id)). @note_counts = project.notes.where(commit_id: @commits.map(&:id)).
......
...@@ -7,31 +7,24 @@ class Projects::DeployKeysController < Projects::ApplicationController ...@@ -7,31 +7,24 @@ class Projects::DeployKeysController < Projects::ApplicationController
layout "project_settings" layout "project_settings"
def index def index
@enabled_keys = @project.deploy_keys @key = DeployKey.new
set_index_vars
@available_keys = accessible_keys - @enabled_keys
@available_project_keys = current_user.project_deploy_keys - @enabled_keys
@available_public_keys = DeployKey.are_public - @enabled_keys
# Public keys that are already used by another accessible project are already
# in @available_project_keys.
@available_public_keys -= @available_project_keys
end end
def new def new
@key = @project.deploy_keys.new redirect_to namespace_project_deploy_keys_path(@project.namespace,
@project)
respond_with(@key)
end end
def create def create
@key = DeployKey.new(deploy_key_params) @key = DeployKey.new(deploy_key_params)
set_index_vars
if @key.valid? && @project.deploy_keys << @key if @key.valid? && @project.deploy_keys << @key
redirect_to namespace_project_deploy_keys_path(@project.namespace, redirect_to namespace_project_deploy_keys_path(@project.namespace,
@project) @project)
else else
render "new" render "index"
end end
end end
...@@ -51,6 +44,18 @@ class Projects::DeployKeysController < Projects::ApplicationController ...@@ -51,6 +44,18 @@ class Projects::DeployKeysController < Projects::ApplicationController
protected protected
def set_index_vars
@enabled_keys ||= @project.deploy_keys
@available_keys ||= accessible_keys - @enabled_keys
@available_project_keys ||= current_user.project_deploy_keys - @enabled_keys
@available_public_keys ||= DeployKey.are_public - @enabled_keys
# Public keys that are already used by another accessible project are already
# in @available_project_keys.
@available_public_keys -= @available_project_keys
end
def accessible_keys def accessible_keys
@accessible_keys ||= current_user.accessible_deploy_keys @accessible_keys ||= current_user.accessible_deploy_keys
end end
......
...@@ -17,7 +17,7 @@ class Projects::GraphsController < Projects::ApplicationController ...@@ -17,7 +17,7 @@ class Projects::GraphsController < Projects::ApplicationController
end end
def commits def commits
@commits = @project.repository.commits(@ref, nil, 2000, 0, true) @commits = @project.repository.commits(@ref, limit: 2000, skip_merges: true)
@commits_graph = Gitlab::Graphs::Commits.new(@commits) @commits_graph = Gitlab::Graphs::Commits.new(@commits)
@commits_per_week_days = @commits_graph.commits_per_week_days @commits_per_week_days = @commits_graph.commits_per_week_days
@commits_per_time = @commits_graph.commits_per_time @commits_per_time = @commits_graph.commits_per_time
...@@ -55,7 +55,7 @@ class Projects::GraphsController < Projects::ApplicationController ...@@ -55,7 +55,7 @@ class Projects::GraphsController < Projects::ApplicationController
private private
def fetch_graph def fetch_graph
@commits = @project.repository.commits(@ref, nil, 6000, 0, true) @commits = @project.repository.commits(@ref, limit: 6000, skip_merges: true)
@log = [] @log = []
@commits.each do |commit| @commits.each do |commit|
......
...@@ -52,8 +52,16 @@ class Projects::HooksController < Projects::ApplicationController ...@@ -52,8 +52,16 @@ class Projects::HooksController < Projects::ApplicationController
end end
def hook_params def hook_params
params.require(:hook).permit(:url, :push_events, :issues_events, params.require(:hook).permit(
:merge_requests_events, :tag_push_events, :note_events, :build_events,
:build_events, :enable_ssl_verification) :enable_ssl_verification,
:issues_events,
:merge_requests_events,
:note_events,
:push_events,
:tag_push_events,
:token,
:url
)
end end
end end
...@@ -3,8 +3,8 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -3,8 +3,8 @@ class Projects::IssuesController < Projects::ApplicationController
include IssuableActions include IssuableActions
before_action :module_enabled before_action :module_enabled
before_action :issue, before_action :issue, only: [:edit, :update, :show, :referenced_merge_requests,
only: [:edit, :update, :show, :referenced_merge_requests, :related_branches] :related_branches, :can_create_branch]
# Allow read any issue # Allow read any issue
before_action :authorize_read_issue!, only: [:show] before_action :authorize_read_issue!, only: [:show]
...@@ -96,6 +96,8 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -96,6 +96,8 @@ class Projects::IssuesController < Projects::ApplicationController
if params[:move_to_project_id].to_i > 0 if params[:move_to_project_id].to_i > 0
new_project = Project.find(params[:move_to_project_id]) new_project = Project.find(params[:move_to_project_id])
return render_404 unless issue.can_move?(current_user, new_project)
move_service = Issues::MoveService.new(project, current_user) move_service = Issues::MoveService.new(project, current_user)
@issue = move_service.execute(@issue, new_project) @issue = move_service.execute(@issue, new_project)
end end
...@@ -139,6 +141,18 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -139,6 +141,18 @@ class Projects::IssuesController < Projects::ApplicationController
end end
end end
def can_create_branch
can_create = current_user &&
can?(current_user, :push_code, @project) &&
@issue.can_be_worked_on?(current_user)
respond_to do |format|
format.json do
render json: { can_create_branch: can_create }
end
end
end
def bulk_update def bulk_update
result = Issues::BulkUpdateService.new(project, current_user, bulk_update_params).execute result = Issues::BulkUpdateService.new(project, current_user, bulk_update_params).execute
redirect_back_or_default(default: { action: 'index' }, options: { notice: "#{result[:count]} issues updated" }) redirect_back_or_default(default: { action: 'index' }, options: { notice: "#{result[:count]} issues updated" })
......
...@@ -40,10 +40,10 @@ class Projects::WikisController < Projects::ApplicationController ...@@ -40,10 +40,10 @@ class Projects::WikisController < Projects::ApplicationController
end end
def update def update
@page = @project_wiki.find_page(params[:id])
return render('empty') unless can?(current_user, :create_wiki, @project) return render('empty') unless can?(current_user, :create_wiki, @project)
@page = @project_wiki.find_page(params[:id])
if @page = WikiPages::UpdateService.new(@project, current_user, wiki_params).execute(@page) if @page = WikiPages::UpdateService.new(@project, current_user, wiki_params).execute(@page)
redirect_to( redirect_to(
namespace_project_wiki_path(@project.namespace, @project, @page), namespace_project_wiki_path(@project.namespace, @project, @page),
......
...@@ -8,6 +8,13 @@ class RegistrationsController < Devise::RegistrationsController ...@@ -8,6 +8,13 @@ class RegistrationsController < Devise::RegistrationsController
def create def create
if !Gitlab::Recaptcha.load_configurations! || verify_recaptcha if !Gitlab::Recaptcha.load_configurations! || verify_recaptcha
# To avoid duplicate form fields on the login page, the registration form
# names fields using `new_user`, but Devise still wants the params in
# `user`.
if params["new_#{resource_name}"].present? && params[resource_name].blank?
params[resource_name] = params.delete(:"new_#{resource_name}")
end
super super
else else
flash[:alert] = "There was an error with the reCAPTCHA code below. Please re-enter the code." flash[:alert] = "There was an error with the reCAPTCHA code below. Please re-enter the code."
......
...@@ -51,7 +51,7 @@ class SnippetsFinder ...@@ -51,7 +51,7 @@ class SnippetsFinder
snippets = project.snippets.fresh snippets = project.snippets.fresh
if current_user if current_user
if project.team.member?(current_user.id) if project.team.member?(current_user.id) || current_user.admin?
snippets snippets
else else
snippets.public_and_internal snippets.public_and_internal
......
...@@ -3,8 +3,8 @@ module BlobHelper ...@@ -3,8 +3,8 @@ module BlobHelper
Gitlab::Highlight.new(blob_name, blob_content, nowrap: nowrap) Gitlab::Highlight.new(blob_name, blob_content, nowrap: nowrap)
end end
def highlight(blob_name, blob_content, nowrap: false) def highlight(blob_name, blob_content, nowrap: false, plain: false)
Gitlab::Highlight.highlight(blob_name, blob_content, nowrap: nowrap) Gitlab::Highlight.highlight(blob_name, blob_content, nowrap: nowrap, plain: plain)
end end
def no_highlight_files def no_highlight_files
......
module CiBadgeHelper
def markdown_badge_code(project, ref)
url = status_ci_project_url(project, ref: ref, format: 'png')
link = namespace_project_commits_path(project.namespace, project, ref)
"[![build status](#{url})](#{link})"
end
def html_badge_code(project, ref)
url = status_ci_project_url(project, ref: ref, format: 'png')
link = namespace_project_commits_path(project.namespace, project, ref)
"<a href='#{link}'><img src='#{url}' /></a>"
end
end
...@@ -23,7 +23,7 @@ module DiffHelper ...@@ -23,7 +23,7 @@ module DiffHelper
end end
def diff_options def diff_options
options = { ignore_whitespace_change: params[:w] == '1' } options = { ignore_whitespace_change: hide_whitespace? }
if diff_hard_limit_enabled? if diff_hard_limit_enabled?
options.merge!(Commit.max_diff_options) options.merge!(Commit.max_diff_options)
end end
...@@ -128,4 +128,31 @@ module DiffHelper ...@@ -128,4 +128,31 @@ module DiffHelper
title title
end end
end end
def commit_diff_whitespace_link(project, commit, options)
url = namespace_project_commit_path(project.namespace, project, commit.id, params_with_whitespace)
toggle_whitespace_link(url, options)
end
def diff_merge_request_whitespace_link(project, merge_request, options)
url = diffs_namespace_project_merge_request_path(project.namespace, project, merge_request, params_with_whitespace)
toggle_whitespace_link(url, options)
end
private
def hide_whitespace?
params[:w] == '1'
end
def params_with_whitespace
hide_whitespace? ? request.query_parameters.except(:w) : request.query_parameters.merge(w: 1)
end
def toggle_whitespace_link(url, options)
options[:class] ||= ''
options[:class] << ' btn btn-default'
link_to "#{hide_whitespace? ? 'Show' : 'Hide'} whitespace changes", url, class: options[:class]
end
end end
...@@ -16,31 +16,49 @@ module IssuesHelper ...@@ -16,31 +16,49 @@ module IssuesHelper
def url_for_project_issues(project = @project, options = {}) def url_for_project_issues(project = @project, options = {})
return '' if project.nil? return '' if project.nil?
if options[:only_path] url =
project.issues_tracker.project_path if options[:only_path]
else project.issues_tracker.project_path
project.issues_tracker.project_url else
end project.issues_tracker.project_url
end
# Ensure we return a valid URL to prevent possible XSS.
URI.parse(url).to_s
rescue URI::InvalidURIError
''
end end
def url_for_new_issue(project = @project, options = {}) def url_for_new_issue(project = @project, options = {})
return '' if project.nil? return '' if project.nil?
if options[:only_path] url =
project.issues_tracker.new_issue_path if options[:only_path]
else project.issues_tracker.new_issue_path
project.issues_tracker.new_issue_url else
end project.issues_tracker.new_issue_url
end
# Ensure we return a valid URL to prevent possible XSS.
URI.parse(url).to_s
rescue URI::InvalidURIError
''
end end
def url_for_issue(issue_iid, project = @project, options = {}) def url_for_issue(issue_iid, project = @project, options = {})
return '' if project.nil? return '' if project.nil?
if options[:only_path] url =
project.issues_tracker.issue_path(issue_iid) if options[:only_path]
else project.issues_tracker.issue_path(issue_iid)
project.issues_tracker.issue_url(issue_iid) else
end project.issues_tracker.issue_url(issue_iid)
end
# Ensure we return a valid URL to prevent possible XSS.
URI.parse(url).to_s
rescue URI::InvalidURIError
''
end end
def bulk_update_milestone_options def bulk_update_milestone_options
......
...@@ -37,7 +37,7 @@ module LabelsHelper ...@@ -37,7 +37,7 @@ module LabelsHelper
link = send("namespace_project_#{type.to_s.pluralize}_path", link = send("namespace_project_#{type.to_s.pluralize}_path",
project.namespace, project.namespace,
project, project,
label_name: label.name) label_name: [label.name])
if block_given? if block_given?
link_to link, &block link_to link, &block
......
...@@ -200,12 +200,8 @@ module ProjectsHelper ...@@ -200,12 +200,8 @@ module ProjectsHelper
end end
def repository_size(project = @project) def repository_size(project = @project)
"#{project.repository_size} MB" size_in_bytes = project.repository_size * 1.megabyte
rescue number_to_human_size(size_in_bytes, delimiter: ',', precision: 2)
# In order to prevent 500 error
# when application cannot allocate memory
# to calculate repo size - just show 'Unknown'
'unknown'
end end
def default_url_to_repo(project = @project) def default_url_to_repo(project = @project)
...@@ -341,4 +337,10 @@ module ProjectsHelper ...@@ -341,4 +337,10 @@ module ProjectsHelper
) )
end end
end end
def sanitize_repo_path(message)
return '' unless message.present?
message.strip.gsub(Gitlab.config.gitlab_shell.repos_path.chomp('/'), "[REPOS PATH]")
end
end end
...@@ -28,6 +28,14 @@ module Emails ...@@ -28,6 +28,14 @@ module Emails
mail_answer_thread(@merge_request, note_thread_options(recipient_id)) mail_answer_thread(@merge_request, note_thread_options(recipient_id))
end end
def note_snippet_email(recipient_id, note_id)
setup_note_mail(note_id, recipient_id)
@snippet = @note.noteable
@target_url = namespace_project_snippet_url(*note_target_url_options)
mail_answer_thread(@snippet, note_thread_options(recipient_id))
end
private private
def note_target_url_options def note_target_url_options
......
...@@ -19,6 +19,14 @@ class Blob < SimpleDelegator ...@@ -19,6 +19,14 @@ class Blob < SimpleDelegator
new(blob) new(blob)
end end
def no_highlighting?
size && size > 1.megabyte
end
def only_display_raw?
size && size > 5.megabytes
end
def svg? def svg?
text? && language && language.name == 'SVG' text? && language && language.name == 'SVG'
end end
......
...@@ -8,7 +8,7 @@ module Milestoneish ...@@ -8,7 +8,7 @@ module Milestoneish
end end
def complete?(user = nil) def complete?(user = nil)
total_items_count(user) == closed_items_count(user) total_items_count(user) > 0 && total_items_count(user) == closed_items_count(user)
end end
def percent_complete(user = nil) def percent_complete(user = nil)
......
...@@ -18,7 +18,7 @@ module Statuseable ...@@ -18,7 +18,7 @@ module Statuseable
WHEN (#{builds})=0 THEN NULL WHEN (#{builds})=0 THEN NULL
WHEN (#{builds})=(#{success})+(#{ignored}) THEN 'success' WHEN (#{builds})=(#{success})+(#{ignored}) THEN 'success'
WHEN (#{builds})=(#{pending}) THEN 'pending' WHEN (#{builds})=(#{pending}) THEN 'pending'
WHEN (#{builds})=(#{canceled}) THEN 'canceled' WHEN (#{builds})=(#{canceled})+(#{success})+(#{ignored}) THEN 'canceled'
WHEN (#{builds})=(#{skipped}) THEN 'skipped' WHEN (#{builds})=(#{skipped}) THEN 'skipped'
WHEN (#{running})+(#{pending})>0 THEN 'running' WHEN (#{running})+(#{pending})>0 THEN 'running'
ELSE 'failed' ELSE 'failed'
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
# note_events :boolean default(FALSE), not null # note_events :boolean default(FALSE), not null
# enable_ssl_verification :boolean default(TRUE) # enable_ssl_verification :boolean default(TRUE)
# build_events :boolean default(FALSE), not null # build_events :boolean default(FALSE), not null
# token :string
# #
class ProjectHook < WebHook class ProjectHook < WebHook
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
# note_events :boolean default(FALSE), not null # note_events :boolean default(FALSE), not null
# enable_ssl_verification :boolean default(TRUE) # enable_ssl_verification :boolean default(TRUE)
# build_events :boolean default(FALSE), not null # build_events :boolean default(FALSE), not null
# token :string
# #
class ServiceHook < WebHook class ServiceHook < WebHook
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
# note_events :boolean default(FALSE), not null # note_events :boolean default(FALSE), not null
# enable_ssl_verification :boolean default(TRUE) # enable_ssl_verification :boolean default(TRUE)
# build_events :boolean default(FALSE), not null # build_events :boolean default(FALSE), not null
# token :string
# #
class SystemHook < WebHook class SystemHook < WebHook
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
# note_events :boolean default(FALSE), not null # note_events :boolean default(FALSE), not null
# enable_ssl_verification :boolean default(TRUE) # enable_ssl_verification :boolean default(TRUE)
# build_events :boolean default(FALSE), not null # build_events :boolean default(FALSE), not null
# token :string
# #
class WebHook < ActiveRecord::Base class WebHook < ActiveRecord::Base
...@@ -43,23 +44,17 @@ class WebHook < ActiveRecord::Base ...@@ -43,23 +44,17 @@ class WebHook < ActiveRecord::Base
if parsed_url.userinfo.blank? if parsed_url.userinfo.blank?
response = WebHook.post(url, response = WebHook.post(url,
body: data.to_json, body: data.to_json,
headers: { headers: build_headers(hook_name),
"Content-Type" => "application/json",
"X-Gitlab-Event" => hook_name.singularize.titleize
},
verify: enable_ssl_verification) verify: enable_ssl_verification)
else else
post_url = url.gsub("#{parsed_url.userinfo}@", "") post_url = url.gsub("#{parsed_url.userinfo}@", '')
auth = { auth = {
username: CGI.unescape(parsed_url.user), username: CGI.unescape(parsed_url.user),
password: CGI.unescape(parsed_url.password), password: CGI.unescape(parsed_url.password),
} }
response = WebHook.post(post_url, response = WebHook.post(post_url,
body: data.to_json, body: data.to_json,
headers: { headers: build_headers(hook_name),
"Content-Type" => "application/json",
"X-Gitlab-Event" => hook_name.singularize.titleize
},
verify: enable_ssl_verification, verify: enable_ssl_verification,
basic_auth: auth) basic_auth: auth)
end end
...@@ -73,4 +68,15 @@ class WebHook < ActiveRecord::Base ...@@ -73,4 +68,15 @@ class WebHook < ActiveRecord::Base
def async_execute(data, hook_name) def async_execute(data, hook_name)
Sidekiq::Client.enqueue(ProjectWebHookWorker, id, data, hook_name) Sidekiq::Client.enqueue(ProjectWebHookWorker, id, data, hook_name)
end end
private
def build_headers(hook_name)
headers = {
'Content-Type' => 'application/json',
'X-Gitlab-Event' => hook_name.singularize.titleize
}
headers['X-Gitlab-Token'] = token if token.present?
headers
end
end end
...@@ -735,19 +735,17 @@ class Project < ActiveRecord::Base ...@@ -735,19 +735,17 @@ class Project < ActiveRecord::Base
end end
def open_branches def open_branches
all_branches = repository.branches # We're using a Set here as checking values in a large Set is faster than
# checking values in a large Array.
protected_set = Set.new(protected_branch_names)
if protected_branches.present? repository.branches.reject do |branch|
all_branches.reject! do |branch| protected_set.include?(branch.name)
protected_branches_names.include?(branch.name)
end
end end
all_branches
end end
def protected_branches_names def protected_branch_names
@protected_branches_names ||= protected_branches.map(&:name) @protected_branch_names ||= protected_branches.pluck(:name)
end end
def root_ref?(branch) def root_ref?(branch)
...@@ -764,7 +762,7 @@ class Project < ActiveRecord::Base ...@@ -764,7 +762,7 @@ class Project < ActiveRecord::Base
# Check if current branch name is marked as protected in the system # Check if current branch name is marked as protected in the system
def protected_branch?(branch_name) def protected_branch?(branch_name)
protected_branches_names.include?(branch_name) protected_branches.where(name: branch_name).any?
end end
def developers_can_push_to_protected_branch?(branch_name) def developers_can_push_to_protected_branch?(branch_name)
...@@ -901,6 +899,7 @@ class Project < ActiveRecord::Base ...@@ -901,6 +899,7 @@ class Project < ActiveRecord::Base
repository.rugged.references.create('HEAD', repository.rugged.references.create('HEAD',
"refs/heads/#{branch}", "refs/heads/#{branch}",
force: true) force: true)
repository.copy_gitattributes(branch)
reload_default_branch reload_default_branch
end end
......
...@@ -26,7 +26,7 @@ class BuildkiteService < CiService ...@@ -26,7 +26,7 @@ class BuildkiteService < CiService
prop_accessor :project_url, :token, :enable_ssl_verification prop_accessor :project_url, :token, :enable_ssl_verification
validates :project_url, presence: true, if: :activated? validates :project_url, presence: true, url: true, if: :activated?
validates :token, presence: true, if: :activated? validates :token, presence: true, if: :activated?
after_save :compose_service_hook, if: :activated? after_save :compose_service_hook, if: :activated?
...@@ -91,7 +91,7 @@ class BuildkiteService < CiService ...@@ -91,7 +91,7 @@ class BuildkiteService < CiService
{ type: 'text', { type: 'text',
name: 'project_url', name: 'project_url',
placeholder: "#{ENDPOINT}/example/project" }, placeholder: "#{ENDPOINT}/example/project" },
{ type: 'checkbox', { type: 'checkbox',
name: 'enable_ssl_verification', name: 'enable_ssl_verification',
title: "Enable SSL verification" } title: "Enable SSL verification" }
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
class IssueTrackerService < Service class IssueTrackerService < Service
validates :project_url, :issues_url, :new_issue_url, presence: true, if: :activated? validates :project_url, :issues_url, :new_issue_url, presence: true, url: true, if: :activated?
default_value_for :category, 'issue_tracker' default_value_for :category, 'issue_tracker'
......
...@@ -28,6 +28,8 @@ class JiraService < IssueTrackerService ...@@ -28,6 +28,8 @@ class JiraService < IssueTrackerService
prop_accessor :username, :password, :api_url, :jira_issue_transition_id, prop_accessor :username, :password, :api_url, :jira_issue_transition_id,
:title, :description, :project_url, :issues_url, :new_issue_url :title, :description, :project_url, :issues_url, :new_issue_url
validates :api_url, presence: true, url: true, if: :activated?
before_validation :set_api_url, :set_jira_issue_transition_id before_validation :set_api_url, :set_jira_issue_transition_id
before_update :reset_password before_update :reset_password
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
class SlackService < Service class SlackService < Service
prop_accessor :webhook, :username, :channel prop_accessor :webhook, :username, :channel
boolean_accessor :notify_only_broken_builds boolean_accessor :notify_only_broken_builds
validates :webhook, presence: true, if: :activated? validates :webhook, presence: true, url: true, if: :activated?
def initialize_properties def initialize_properties
if properties.nil? if properties.nil?
......
...@@ -22,4 +22,6 @@ class ProjectSnippet < Snippet ...@@ -22,4 +22,6 @@ class ProjectSnippet < Snippet
# Scopes # Scopes
scope :fresh, -> { order("created_at DESC") } scope :fresh, -> { order("created_at DESC") }
participant :author, :notes
end end
...@@ -87,13 +87,15 @@ class Repository ...@@ -87,13 +87,15 @@ class Repository
nil nil
end end
def commits(ref, path = nil, limit = nil, offset = nil, skip_merges = false) def commits(ref, path: nil, limit: nil, offset: nil, skip_merges: false, after: nil, before: nil)
options = { options = {
repo: raw_repository, repo: raw_repository,
ref: ref, ref: ref,
path: path, path: path,
limit: limit, limit: limit,
offset: offset, offset: offset,
after: after,
before: before,
# --follow doesn't play well with --skip. See: # --follow doesn't play well with --skip. See:
# https://gitlab.com/gitlab-org/gitlab-ce/issues/3574#note_3040520 # https://gitlab.com/gitlab-org/gitlab-ce/issues/3574#note_3040520
follow: false, follow: false,
...@@ -146,10 +148,20 @@ class Repository ...@@ -146,10 +148,20 @@ class Repository
find_branch(branch_name) find_branch(branch_name)
end end
def add_tag(tag_name, ref, message = nil) def add_tag(user, tag_name, target, message = nil)
before_push_tag oldrev = Gitlab::Git::BLANK_SHA
ref = Gitlab::Git::TAG_REF_PREFIX + tag_name
target = commit(target).try(:id)
return false unless target
options = { message: message, tagger: user_to_committer(user) } if message
GitHooksService.new.execute(user, path_to_repo, oldrev, target, ref) do
rugged.tags.create(tag_name, target, options)
end
gitlab_shell.add_tag(path_with_namespace, tag_name, ref, message) find_tag(tag_name)
end end
def rm_branch(user, branch_name) def rm_branch(user, branch_name)
...@@ -457,7 +469,7 @@ class Repository ...@@ -457,7 +469,7 @@ class Repository
def changelog def changelog
cache.fetch(:changelog) do cache.fetch(:changelog) do
tree(:head).blobs.find do |file| tree(:head).blobs.find do |file|
file.name =~ /\A(changelog|history)/i file.name =~ /\A(changelog|history|changes|news)/i
end end
end end
end end
...@@ -575,7 +587,7 @@ class Repository ...@@ -575,7 +587,7 @@ class Repository
end end
def contributors def contributors
commits = self.commits(nil, nil, 2000, 0, true) commits = self.commits(nil, limit: 2000, offset: 0, skip_merges: true)
commits.group_by(&:author_email).map do |email, commits| commits.group_by(&:author_email).map do |email, commits|
contributor = Gitlab::Contributor.new contributor = Gitlab::Contributor.new
...@@ -938,6 +950,16 @@ class Repository ...@@ -938,6 +950,16 @@ class Repository
raw_repository.ls_files(actual_ref) raw_repository.ls_files(actual_ref)
end end
def copy_gitattributes(ref)
actual_ref = ref || root_ref
begin
raw_repository.copy_gitattributes(actual_ref)
true
rescue Gitlab::Git::Repository::InvalidRef
false
end
end
def main_language def main_language
return if empty? || rugged.head_unborn? return if empty? || rugged.head_unborn?
......
...@@ -112,6 +112,10 @@ class Snippet < ActiveRecord::Base ...@@ -112,6 +112,10 @@ class Snippet < ActiveRecord::Base
visibility_level visibility_level
end end
def no_highlighting?
content.lines.count > 1000
end
class << self class << self
# Searches for snippets with a matching title or file name. # Searches for snippets with a matching title or file name.
# #
......
...@@ -43,9 +43,4 @@ class CreateBranchService < BaseService ...@@ -43,9 +43,4 @@ class CreateBranchService < BaseService
out[:branch] = branch out[:branch] = branch
out out
end end
def build_push_data(project, user, branch)
Gitlab::PushDataBuilder.
build(project, user, Gitlab::Git::BLANK_SHA, branch.target, "#{Gitlab::Git::BRANCH_REF_PREFIX}#{branch.name}", [])
end
end end
require_relative 'base_service' require_relative 'base_service'
class CreateTagService < BaseService class CreateTagService < BaseService
def execute(tag_name, ref, message, release_description = nil) def execute(tag_name, target, message, release_description = nil)
valid_tag = Gitlab::GitRefValidator.validate(tag_name) valid_tag = Gitlab::GitRefValidator.validate(tag_name)
if valid_tag == false return error('Tag name invalid') unless valid_tag
return error('Tag name invalid')
end
repository = project.repository repository = project.repository
existing_tag = repository.find_tag(tag_name)
if existing_tag
return error('Tag already exists')
end
message.strip! if message message.strip! if message
repository.add_tag(tag_name, ref, message) new_tag = nil
new_tag = repository.find_tag(tag_name) begin
new_tag = repository.add_tag(current_user, tag_name, target, message)
rescue Rugged::TagError
return error("Tag #{tag_name} already exists")
rescue GitHooksService::PreReceiveError
return error('Tag creation was rejected by Git hook')
end
if new_tag if new_tag
push_data = create_push_data(project, current_user, new_tag)
EventCreateService.new.push(project, current_user, push_data)
project.execute_hooks(push_data.dup, :tag_push_hooks)
project.execute_services(push_data.dup, :tag_push_hooks)
CreateCommitBuildsService.new.execute(project, current_user, push_data)
if release_description if release_description
CreateReleaseService.new(@project, @current_user). CreateReleaseService.new(@project, @current_user).
execute(tag_name, release_description) execute(tag_name, release_description)
end end
success.merge(tag: new_tag)
success(new_tag)
else else
error('Invalid reference name') error("Target #{target} is invalid")
end end
end end
def success(branch)
out = super()
out[:tag] = branch
out
end
def create_push_data(project, user, tag)
commits = [project.commit(tag.target)].compact
Gitlab::PushDataBuilder.
build(project, user, Gitlab::Git::BLANK_SHA, tag.target, "#{Gitlab::Git::TAG_REF_PREFIX}#{tag.name}", commits, tag.message)
end
end end
...@@ -17,6 +17,7 @@ class GitPushService < BaseService ...@@ -17,6 +17,7 @@ class GitPushService < BaseService
# 6. Checks if the project's main language has changed # 6. Checks if the project's main language has changed
# #
def execute def execute
@project.repository.after_create if @project.empty_repo?
@project.repository.after_push_commit(branch_name, params[:newrev]) @project.repository.after_push_commit(branch_name, params[:newrev])
if push_remove_branch? if push_remove_branch?
...@@ -42,7 +43,12 @@ class GitPushService < BaseService ...@@ -42,7 +43,12 @@ class GitPushService < BaseService
# Collect data for this git push # Collect data for this git push
@push_commits = @project.repository.commits_between(params[:oldrev], params[:newrev]) @push_commits = @project.repository.commits_between(params[:oldrev], params[:newrev])
process_commit_messages process_commit_messages
# Update the bare repositories info/attributes file using the contents of the default branches
# .gitattributes file
update_gitattributes if is_default_branch?
end end
# Update merge requests that may be affected by this push. A new branch # Update merge requests that may be affected by this push. A new branch
# could cause the last commit of a merge request to change. # could cause the last commit of a merge request to change.
update_merge_requests update_merge_requests
...@@ -54,6 +60,10 @@ class GitPushService < BaseService ...@@ -54,6 +60,10 @@ class GitPushService < BaseService
perform_housekeeping perform_housekeeping
end end
def update_gitattributes
@project.repository.copy_gitattributes(params[:ref])
end
def update_main_language def update_main_language
# Performance can be bad so for now only check main_language once # Performance can be bad so for now only check main_language once
# See https://gitlab.com/gitlab-org/gitlab-ce/issues/14937 # See https://gitlab.com/gitlab-org/gitlab-ce/issues/14937
......
...@@ -2,6 +2,7 @@ class GitTagPushService < BaseService ...@@ -2,6 +2,7 @@ class GitTagPushService < BaseService
attr_accessor :push_data attr_accessor :push_data
def execute def execute
project.repository.after_create if project.empty_repo?
project.repository.before_push_tag project.repository.before_push_tag
@push_data = build_push_data @push_data = build_push_data
......
...@@ -7,6 +7,9 @@ module MergeRequests ...@@ -7,6 +7,9 @@ module MergeRequests
merge_request.can_be_created = false merge_request.can_be_created = false
merge_request.compare_commits = [] merge_request.compare_commits = []
merge_request.source_project = project unless merge_request.source_project merge_request.source_project = project unless merge_request.source_project
merge_request.target_project = nil unless can?(current_user, :read_project, merge_request.target_project)
merge_request.target_project ||= (project.forked_from_project || project) merge_request.target_project ||= (project.forked_from_project || project)
merge_request.target_branch ||= merge_request.target_project.default_branch merge_request.target_branch ||= merge_request.target_project.default_branch
......
...@@ -5,6 +5,8 @@ module Notes ...@@ -5,6 +5,8 @@ module Notes
note.author = current_user note.author = current_user
note.system = false note.system = false
return unless valid_project?(note)
if note.save if note.save
# Finish the harder work in the background # Finish the harder work in the background
NewNoteWorker.perform_in(2.seconds, note.id, params) NewNoteWorker.perform_in(2.seconds, note.id, params)
...@@ -13,5 +15,14 @@ module Notes ...@@ -13,5 +15,14 @@ module Notes
note note
end end
private
def valid_project?(note)
return false unless project
return true if note.for_commit?
note.noteable.try(:project) == project
end
end end
end end
module WikiPages module WikiPages
class CreateService < WikiPages::BaseService class CreateService < WikiPages::BaseService
def execute def execute
page = WikiPage.new(@project.wiki) project_wiki = ProjectWiki.new(@project, current_user)
page = WikiPage.new(project_wiki)
if page.create(@params) if page.create(@params)
execute_hooks(page, 'create') execute_hooks(page, 'create')
......
...@@ -13,9 +13,15 @@ ...@@ -13,9 +13,15 @@
= form_errors(@hook) = form_errors(@hook)
.form-group .form-group
= f.label :url, "URL:", class: 'control-label' = f.label :url, 'URL', class: 'control-label'
.col-sm-10 .col-sm-10
= f.text_field :url, class: "form-control" = f.text_field :url, class: 'form-control'
.form-group
= f.label :token, 'Secret Token', class: 'control-label'
.col-sm-10
= f.text_field :token, class: 'form-control'
%p.help-block
Use this token to validate received payloads
.form-group .form-group
= f.label :url, "Trigger", class: 'control-label' = f.label :url, "Trigger", class: 'control-label'
.col-sm-10.prepend-top-10 .col-sm-10.prepend-top-10
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
%h3 Two-factor Authentication %h3 Two-factor Authentication
.login-body .login-body
= form_for(resource, as: resource_name, url: session_path(resource_name), method: :post) do |f| = form_for(resource, as: resource_name, url: session_path(resource_name), method: :post) do |f|
= f.text_field :otp_attempt, class: 'form-control', placeholder: 'Two-factor authentication code', required: true, autofocus: true = f.text_field :otp_attempt, class: 'form-control', placeholder: 'Two-factor Authentication code', required: true, autofocus: true
%p.help-block.hint If you've lost your phone, you may enter one of your recovery codes. %p.help-block.hint Enter the code from the two-factor app on your mobile device. If you've lost your device, you may enter one of your recovery codes.
.prepend-top-20 .prepend-top-20
= f.submit "Verify code", class: "btn btn-save" = f.submit "Verify code", class: "btn btn-save"
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
.login-heading .login-heading
%h3 Create an account %h3 Create an account
.login-body .login-body
= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| = form_for(resource, as: "new_#{resource_name}", url: registration_path(resource_name)) do |f|
.devise-errors .devise-errors
= devise_error_messages! = devise_error_messages!
%div %div
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
%div %div
= f.email_field :email, class: "form-control middle", placeholder: "Email", required: true = f.email_field :email, class: "form-control middle", placeholder: "Email", required: true
.form-group.append-bottom-20#password-strength .form-group.append-bottom-20#password-strength
= f.password_field :password, class: "form-control bottom", id: "user_password_sign_up", placeholder: "Password", required: true = f.password_field :password, class: "form-control bottom", placeholder: "Password", required: true
%div %div
- if current_application_settings.recaptcha_enabled - if current_application_settings.recaptcha_enabled
= recaptcha_tags = recaptcha_tags
......
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
= icon('pencil') = icon('pencil')
= render 'delete_form', application: application, small: true = render 'delete_form', application: application, small: true
- else - else
.profile-settings-message.text-center .settings-message.text-center
You don't have any applications You don't have any applications
.oauth-authorized-applications.prepend-top-20.append-bottom-default .oauth-authorized-applications.prepend-top-20.append-bottom-default
- if user_oauth_applications? - if user_oauth_applications?
...@@ -78,5 +78,5 @@ ...@@ -78,5 +78,5 @@
%td= token.scopes %td= token.scopes
%td= render 'doorkeeper/authorized_applications/delete_form', token: token %td= render 'doorkeeper/authorized_applications/delete_form', token: token
- else - else
.profile-settings-message.text-center .settings-message.text-center
You don't have any authorized applications You don't have any authorized applications
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
= cache [event, current_application_settings, "v2.2"] do = cache [event, current_application_settings, "v2.2"] do
- if event.author - if event.author
= link_to user_path(event.author.username) do = link_to user_path(event.author) do
= image_tag avatar_icon(event.author_email, 40), class: "avatar s40", alt:'' = image_tag avatar_icon(event.author_email, 40), class: "avatar s40", alt:''
- else - else
= image_tag avatar_icon(event.author_email, 40), class: "avatar s40", alt:'' = image_tag avatar_icon(event.author_email, 40), class: "avatar s40", alt:''
......
...@@ -22,13 +22,13 @@ ...@@ -22,13 +22,13 @@
= image_tag avatar_icon(current_user, 60), alt: 'Profile', class: 'avatar avatar s36' = image_tag avatar_icon(current_user, 60), alt: 'Profile', class: 'avatar avatar s36'
.username .username
= current_user.username = current_user.username
- if defined?(nav) && nav
.layout-nav
.container-fluid
= render "layouts/nav/#{nav}"
.content-wrapper .content-wrapper
= render "layouts/flash" = render "layouts/flash"
= yield :flash_message = yield :flash_message
- if defined?(nav) && nav
.layout-nav
%div{ class: container_class }
= render "layouts/nav/#{nav}"
%div{ class: (container_class unless @no_container) } %div{ class: (container_class unless @no_container) }
.content .content
.clearfix .clearfix
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
- if current_user - if current_user
- if session[:impersonator_id] - if session[:impersonator_id]
%li.impersonation %li.impersonation
= link_to stop_impersonation_admin_users_path, method: :delete, title: 'Stop Impersonation', data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do = link_to admin_impersonation_path, method: :delete, title: 'Stop Impersonation', data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do
= icon('user-secret fw') = icon('user-secret fw')
- if current_user.is_admin? - if current_user.is_admin?
%li %li
......
New comment for Snippet <%= @snippet.id %>
<%= url_for(namespace_project_snippet_url(@snippet.project.namespace, @snippet.project, @snippet, anchor: "note_#{@note.id}")) %>
Author: <%= @note.author_name %>
<%= @note.note %>
...@@ -45,4 +45,4 @@ ...@@ -45,4 +45,4 @@
%span.label.label-info Public Email %span.label.label-info Public Email
- if email.email === current_user.notification_email - if email.email === current_user.notification_email
%span.label.label-info Notification Email %span.label.label-info Notification Email
= link_to 'Remove', profile_email_path(email), data: { confirm: 'Are you sure?'}, method: :delete, class: 'btn btn-sm btn-remove pull-right' = link_to 'Remove', profile_email_path(email), data: { confirm: 'Are you sure?'}, method: :delete, class: 'btn btn-sm btn-warning prepend-left-10'
%li.key-list-item %li.key-list-item
.pull-left.append-right-10 .pull-left.append-right-10
= icon 'key', class: "key-icon hidden-xs" = icon 'key', class: "settings-list-icon hidden-xs"
.key-list-item-info .key-list-item-info
= link_to path_to_key(key, is_admin), class: "title" do = link_to path_to_key(key, is_admin), class: "title" do
= key.title = key.title
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
%ul.well-list %ul.well-list
= render partial: 'profiles/keys/key', collection: @keys, locals: { is_admin: is_admin } = render partial: 'profiles/keys/key', collection: @keys, locals: { is_admin: is_admin }
- else - else
%p.profile-settings-message.text-center %p.settings-message.text-center
- if is_admin - if is_admin
There are no SSH keys associated with this account. There are no SSH keys associated with this account.
- else - else
......
...@@ -8,11 +8,11 @@ ...@@ -8,11 +8,11 @@
%p %p
- if @user.avatar? - if @user.avatar?
You can change your avatar here You can change your avatar here
- if Gitlab.config.gravatar.enabled - if gravatar_enabled?
or remove the current avatar to revert to #{link_to Gitlab.config.gravatar.host, "http://" + Gitlab.config.gravatar.host} or remove the current avatar to revert to #{link_to Gitlab.config.gravatar.host, "http://" + Gitlab.config.gravatar.host}
- else - else
You can upload an avatar here You can upload an avatar here
- if Gitlab.config.gravatar.enabled - if gravatar_enabled?
or change it at #{link_to Gitlab.config.gravatar.host, "http://" + Gitlab.config.gravatar.host} or change it at #{link_to Gitlab.config.gravatar.host, "http://" + Gitlab.config.gravatar.host}
.col-lg-9 .col-lg-9
.clearfix.avatar-image.append-bottom-default .clearfix.avatar-image.append-bottom-default
......
- blob.load_all_data!(@repository) - if blob.only_display_raw?
- if markup?(blob.name) .file-content.code
.file-content.wiki .nothing-here-block
= render_markup(blob.name, blob.data) File too large, you can
= succeed '.' do
= link_to 'view the raw file', namespace_project_raw_path(@project.namespace, @project, @id), target: '_blank'
- else - else
- unless blob.empty? - blob.load_all_data!(@repository)
= render 'shared/file_highlight', blob: blob
- if markup?(blob.name)
.file-content.wiki
= render_markup(blob.name, blob.data)
- else - else
.file-content.code - if blob.empty?
.nothing-here-block Empty file .file-content.code
.nothing-here-block Empty file
- else
= render 'shared/file_highlight', blob: blob
%li %li
.pull-right .pull-left.append-right-10.hidden-xs
= icon "key", class: "key-icon"
.deploy-key-content.key-list-item-info
%strong.title
= deploy_key.title
.description
= deploy_key.fingerprint
.deploy-key-content.prepend-left-default.deploy-key-projects
- deploy_key.projects.each do |project|
- if can?(current_user, :read_project, project)
= link_to namespace_project_path(project.namespace, project), class: "label deploy-project-label" do
= project.name_with_namespace
.deploy-key-content
%span.key-created-at
created #{time_ago_with_tooltip(deploy_key.created_at)}
.visible-xs-block.visible-sm-block
- if @available_keys.include?(deploy_key) - if @available_keys.include?(deploy_key)
= link_to enable_namespace_project_deploy_key_path(@project.namespace, @project, deploy_key), class: 'btn btn-sm', method: :put do = link_to enable_namespace_project_deploy_key_path(@project.namespace, @project, deploy_key), class: "btn btn-sm prepend-left-10", method: :put do
= icon('plus')
Enable Enable
- else - else
- if deploy_key.destroyed_when_orphaned? && deploy_key.almost_orphaned? - if deploy_key.destroyed_when_orphaned? && deploy_key.almost_orphaned?
= link_to 'Remove', disable_namespace_project_deploy_key_path(@project.namespace, @project, deploy_key), data: { confirm: 'You are going to remove deploy key. Are you sure?'}, method: :put, class: "btn btn-remove delete-key btn-sm pull-right" = link_to disable_namespace_project_deploy_key_path(@project.namespace, @project, deploy_key), data: { confirm: "You are going to remove deploy key. Are you sure?" }, method: :put, class: "btn btn-warning btn-sm prepend-left-10" do
Remove
- else - else
= link_to disable_namespace_project_deploy_key_path(@project.namespace, @project, deploy_key), class: 'btn btn-sm', method: :put do = link_to disable_namespace_project_deploy_key_path(@project.namespace, @project, deploy_key), class: "btn btn-warning btn-sm prepend-left-10", method: :put do
= icon('power-off')
Disable Disable
= icon('key')
%strong= deploy_key.title
%br
%code.key-fingerprint= deploy_key.fingerprint
%p.light.prepend-top-10
- if deploy_key.public?
%span.label.label-info.deploy-project-label
Public deploy key
- deploy_key.projects.each do |project|
- if can?(current_user, :read_project, project)
%span.label.label-gray.deploy-project-label
= link_to namespace_project_path(project.namespace, project) do
= project.name_with_namespace
%small.pull-right
Created #{time_ago_with_tooltip(deploy_key.created_at)}
%div = form_for [@project.namespace.becomes(Namespace), @project, @key], url: namespace_project_deploy_keys_path, html: { class: "js-requires-input" } do |f|
= form_for [@project.namespace.becomes(Namespace), @project, @key], url: namespace_project_deploy_keys_path, html: { class: 'deploy-key-form form-horizontal js-requires-input' } do |f| = form_errors(@key)
= form_errors(@key) .form-group
= f.label :title, class: "label-light"
.form-group = f.text_field :title, class: 'form-control', autofocus: true, required: true
= f.label :title, class: "control-label" .form-group
.col-sm-10= f.text_field :title, class: 'form-control', autofocus: true, required: true = f.label :key, class: "label-light"
.form-group = f.text_area :key, class: "form-control", rows: 5, required: true
= f.label :key, class: "control-label" .form-group
.col-sm-10 %p.light.append-bottom-0
%p.light Paste a machine public key here. Read more about how to generate it
Paste a machine public key here. Read more about how to generate it = link_to "here", help_page_path("ssh", "README")
= link_to "here", help_page_path("ssh", "README") = f.submit "Add key", class: "btn-create btn"
= f.text_area :key, class: "form-control thin_area", rows: 5, required: true
.form-actions
= f.submit 'Create Deploy Key', class: "btn-create btn"
= link_to "Cancel", namespace_project_deploy_keys_path(@project.namespace, @project), class: "btn btn-cancel"
- page_title "Deploy Keys" - page_title "Deploy Keys"
%h3.page-title .row.prepend-top-default
Deploy keys allow read-only access to the repository .col-lg-3.profile-settings-sidebar
%h4.prepend-top-0
= link_to new_namespace_project_deploy_key_path(@project.namespace, @project), class: "btn btn-new pull-right", title: "New Deploy Key" do = page_title
%i.fa.fa-plus %p
New Deploy Key Deploy keys allow read-only access to your repository. Deploy keys can be used for CI, staging or production servers. You can create a deploy key or add an existing one.
.col-lg-9
%p.light %h5.prepend-top-0
Deploy keys can be used for CI, staging or production servers. Create a new deploy key for this project
You can create a deploy key or add an existing one = render "form"
.col-lg-9.col-lg-offset-3
%hr.clearfix %hr
.col-lg-9.col-lg-offset-3.append-bottom-default.deploy-keys
.row %h5.prepend-top-0
.col-md-6.enabled-keys Enabled deploy keys for this project (#{@enabled_keys.size})
%h5 - if @enabled_keys.any?
%strong.cgreen Enabled deploy keys %ul.well-list
for this project = render @enabled_keys
%ul.bordered-list - else
= render @enabled_keys .profile-settings-message.text-center
- if @enabled_keys.blank? No deploy keys found. Create one with the form above or add existing one below.
.light-well %h5.prepend-top-default
.nothing-here-block Create a #{link_to 'new deploy key', new_namespace_project_deploy_key_path(@project.namespace, @project)} or add an existing one Deploy keys from projects you have access to (#{@available_project_keys.size})
.col-md-6.available-keys - if @available_project_keys.any?
- # If there are available public deploy keys but no available project deploy keys, only public deploy keys are shown. %ul.well-list
- if @available_project_keys.any? || @available_public_keys.blank?
%h5
%strong Deploy keys
from projects you have access to
%ul.bordered-list
= render @available_project_keys = render @available_project_keys
- if @available_project_keys.blank? - else
.light-well .profile-settings-message.text-center
.nothing-here-block Deploy keys from projects you have access to will be displayed here No deploy keys from your projects could be found. Create one with the form above or add existing one below.
- if @available_public_keys.any? - if @available_public_keys.any?
%h5 %h5.prepend-top-default
%strong Public deploy keys Public deploy keys available to any project (#{@available_public_keys.size})
available to any project %ul.well-list
%ul.bordered-list
= render @available_public_keys = render @available_public_keys
- show_whitespace_toggle = local_assigns.fetch(:show_whitespace_toggle, true)
- if diff_view == 'parallel' - if diff_view == 'parallel'
- fluid_layout true - fluid_layout true
...@@ -5,6 +6,11 @@ ...@@ -5,6 +6,11 @@
.content-block.oneline-block.files-changed .content-block.oneline-block.files-changed
.inline-parallel-buttons .inline-parallel-buttons
- if show_whitespace_toggle
- if current_controller?(:commit)
= commit_diff_whitespace_link(@project, @commit, class: 'hidden-xs')
- elsif current_controller?(:merge_requests)
= diff_merge_request_whitespace_link(@project, @merge_request, class: 'hidden-xs')
.btn-group .btn-group
= inline_diff_btn = inline_diff_btn
= parallel_diff_btn = parallel_diff_btn
......
...@@ -40,19 +40,19 @@ ...@@ -40,19 +40,19 @@
= view_file_btn(diff_commit.id, diff_file, project) = view_file_btn(diff_commit.id, diff_file, project)
.diff-content.diff-wrap-lines .diff-content.diff-wrap-lines
-# Skipp all non non-supported blobs - # Skip all non non-supported blobs
- return unless blob.respond_to?('text?') - return unless blob.respond_to?('text?')
- if diff_file.too_large? - if diff_file.too_large?
.nothing-here-block .nothing-here-block This diff could not be displayed because it is too large.
This diff could not be displayed because it is too large. - elsif blob_text_viewable?(blob) && !project.repository.diffable?(blob)
- else .nothing-here-block This diff was suppressed by a .gitattributes entry.
- if blob_text_viewable?(blob) - elsif blob_text_viewable?(blob)
- if diff_view == 'parallel' - if diff_view == 'parallel'
= render "projects/diffs/parallel_view", diff_file: diff_file, project: project, blob: blob, index: i = render "projects/diffs/parallel_view", diff_file: diff_file, project: project, blob: blob, index: i
- else
= render "projects/diffs/text_file", diff_file: diff_file, index: i
- elsif blob.image?
- old_file = project.repository.prev_blob_for_diff(diff_commit, diff_file)
= render "projects/diffs/image", diff_file: diff_file, old_file: old_file, file: blob, index: i, diff_refs: diff_refs
- else - else
.nothing-here-block No preview for this file type = render "projects/diffs/text_file", diff_file: diff_file, index: i
- elsif blob.image?
- old_file = project.repository.prev_blob_for_diff(diff_commit, diff_file)
= render "projects/diffs/image", diff_file: diff_file, old_file: old_file, file: blob, index: i, diff_refs: diff_refs
- else
.nothing-here-block No preview for this file type
- page_title "Groups" - page_title "Groups"
%h3.page_title Share project with other groups .row.prepend-top-default
%p.light .col-lg-3.settings-sidebar
Projects can be stored in only one group at once. However you can share a project with other groups here. %h4.prepend-top-0
%hr Share project with other groups
- if @group_links.present? %p
.enabled-groups.panel.panel-default Projects can be stored in only one group at once. However you can share a project with other groups here.
.panel-heading .col-lg-9
Already shared with %h5.prepend-top-0
%ul.well-list Set a group to share
- @group_links.each do |group_link| = form_tag namespace_project_group_links_path(@project.namespace, @project), method: :post do
- group = group_link.group
%li
.pull-right
= link_to namespace_project_group_link_path(@project.namespace, @project, group_link), method: :delete, class: 'btn btn-sm' do
%i.icon-remove
disable sharing
= link_to group do
%strong
%i.icon-folder-open
= group.name
%br
.light up to #{group_link.human_access}
.available-groups
%h4
Can be shared with
%div
= form_tag namespace_project_group_links_path(@project.namespace, @project), method: :post, class: 'form-horizontal' do
.form-group .form-group
= label_tag :link_group_id, 'Group', class: 'control-label' = label_tag :link_group_id, "Group", class: "label-light"
.col-sm-10 = groups_select_tag(:link_group_id, skip_group: @project.group.try(:path))
= groups_select_tag(:link_group_id, skip_group: @project.group.try(:path))
.form-group .form-group
= label_tag :link_group_access, 'Max access level', class: 'control-label' = label_tag :link_group_access, "Max access level", class: "label-light"
.col-sm-10 .select-wrapper
= select_tag :link_group_access, options_for_select(ProjectGroupLink.access_options, ProjectGroupLink.default_access), class: "form-control" = select_tag :link_group_access, options_for_select(ProjectGroupLink.access_options, ProjectGroupLink.default_access), class: "form-control select-control"
.form-actions %span.caret
= submit_tag "Share", class: "btn btn-create" = submit_tag "Share", class: "btn btn-create"
.col-lg-9.col-lg-offset-3
%hr
.col-lg-9.col-lg-offset-3.append-bottom-default.enabled-groups
%h5.prepend-top-0
Groups you share with (#{@group_links.size})
- if @group_links.present?
%ul.well-list
- @group_links.each do |group_link|
- group = group_link.group
%li
.pull-left.append-right-10.hidden-xs
= icon("folder-open-o", class: "settings-list-icon")
.pull-left
= link_to group do
= group.name
%br
up to #{group_link.human_access}
.pull-right
= link_to namespace_project_group_link_path(@project.namespace, @project, group_link), method: :delete, class: "btn btn-transparent" do
%span.sr-only disable sharing
= icon("trash")
- else
.settings-message.text-center
There are no groups with access to your project, add one in the form above
%li
.row
.col-md-8.col-lg-7
%strong.light-header= hook.url
%div
- %w(push_events tag_push_events issues_events note_events merge_requests_events build_events).each do |trigger|
- if hook.send(trigger)
%span.label.label-gray.deploy-project-label= trigger.titleize
.col-md-4.col-lg-5.text-right-lg.prepend-top-5
%span.append-right-10.inline
SSL Verification: #{hook.enable_ssl_verification ? "enabled" : "disabled"}
= link_to "Test", test_namespace_project_hook_path(@project.namespace, @project, hook), class: "btn btn-sm"
= link_to namespace_project_hook_path(@project.namespace, @project, hook), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-transparent" do
%span.sr-only Remove
= icon('trash')
- page_title "Webhooks" - page_title "Webhooks"
%h3.page-title .row.prepend-top-default
Webhooks .col-lg-3.profile-settings-sidebar
%h4.prepend-top-0
= page_title
%p
#{link_to "Webhooks", help_page_path("web_hooks", "web_hooks")} can be
used for binding events when something is happening within the project.
.col-lg-9.append-bottom-default
%h5.prepend-top-0
Add new webhook
= form_for [@project.namespace.becomes(Namespace), @project, @hook], as: :hook, url: namespace_project_hooks_path(@project.namespace, @project) do |f|
= form_errors(@hook)
%p.light .form-group
#{link_to "Webhooks ", help_page_path("web_hooks", "web_hooks"), class: "vlink"} can be = f.label :url, "URL", class: "label-light"
used for binding events when something is happening within the project. = f.text_field :url, class: "form-control", placeholder: "http://example.com/trigger-ci.json"
.form-group
%hr.clearfix = f.label :token, "Secret Token", class: 'label-light'
= f.text_field :token, class: "form-control", placeholder: ''
= form_for [@project.namespace.becomes(Namespace), @project, @hook], as: :hook, url: namespace_project_hooks_path(@project.namespace, @project), html: { class: 'form-horizontal' } do |f| %p.help-block
= form_errors(@hook) Use this token to validate received payloads
.form-group
.form-group = f.label :url, "Trigger", class: "label-light"
= f.label :url, "URL", class: 'control-label' %div
.col-sm-10 = f.check_box :push_events, class: "pull-left"
= f.text_field :url, class: "form-control", placeholder: 'http://example.com/trigger-ci.json' .prepend-left-20
.form-group = f.label :push_events, class: "label-light append-bottom-0" do
= f.label :url, "Trigger", class: 'control-label' Push events
.col-sm-10.prepend-top-10 %p.light
%div This url will be triggered by a push to the repository
= f.check_box :push_events, class: 'pull-left' %div
.prepend-left-20 = f.check_box :tag_push_events, class: "pull-left"
= f.label :push_events, class: 'list-label' do .prepend-left-20
%strong Push events = f.label :tag_push_events, class: "label-light append-bottom-0" do
%p.light Tag push events
This url will be triggered by a push to the repository %p.light
%div This url will be triggered when a new tag is pushed to the repository
= f.check_box :tag_push_events, class: 'pull-left' %div
.prepend-left-20 = f.check_box :note_events, class: "pull-left"
= f.label :tag_push_events, class: 'list-label' do .prepend-left-20
%strong Tag push events = f.label :note_events, class: "label-light append-bottom-0" do
%p.light Comments
This url will be triggered when a new tag is pushed to the repository %p.light
%div This url will be triggered when someone adds a comment
= f.check_box :note_events, class: 'pull-left' %div
.prepend-left-20 = f.check_box :issues_events, class: "pull-left"
= f.label :note_events, class: 'list-label' do .prepend-left-20
%strong Comments = f.label :issues_events, class: "label-light append-bottom-0" do
%p.light Issues events
This url will be triggered when someone adds a comment %p.light
%div This url will be triggered when an issue is created/updated/merged
= f.check_box :issues_events, class: 'pull-left' %div
.prepend-left-20 = f.check_box :merge_requests_events, class: "pull-left"
= f.label :issues_events, class: 'list-label' do .prepend-left-20
%strong Issues events = f.label :merge_requests_events, class: "label-light append-bottom-0" do
%p.light Merge Request events
This url will be triggered when an issue is created/updated/merged %p.light
%div This url will be triggered when a merge request is created/updated/merged
= f.check_box :merge_requests_events, class: 'pull-left' %div
.prepend-left-20 = f.check_box :build_events, class: "pull-left"
= f.label :merge_requests_events, class: 'list-label' do .prepend-left-20
%strong Merge Request events = f.label :build_events, class: "label-light append-bottom-0" do
%p.light Build events
This url will be triggered when a merge request is created/updated/merged %p.light
%div This url will be triggered when the build status changes
= f.check_box :build_events, class: 'pull-left' .form-group
.prepend-left-20 = f.label :enable_ssl_verification, "SSL verification", class: "label-light"
= f.label :build_events, class: 'list-label' do %div
%strong Build events = f.check_box :enable_ssl_verification, class: "pull-left"
%p.light .prepend-left-20
This url will be triggered when the build status changes = f.label :enable_ssl_verification, class: "label-light append-bottom-0" do
.form-group Enable SSL verification
= f.label :enable_ssl_verification, "SSL verification", class: 'control-label checkbox' = f.submit "Add Webhook", class: "btn btn-create"
.col-sm-10 %hr
.checkbox %h5.prepend-top-default
= f.label :enable_ssl_verification do
= f.check_box :enable_ssl_verification
%strong Enable SSL verification
.form-actions
= f.submit "Add Webhook", class: "btn btn-create"
-if @hooks.any?
.panel.panel-default
.panel-heading
Webhooks (#{@hooks.count}) Webhooks (#{@hooks.count})
%ul.content-list - if @hooks.any?
- @hooks.each do |hook| %ul.well-list
%li - @hooks.each do |hook|
.controls = render "project_hook", hook: hook
= link_to 'Test Hook', test_namespace_project_hook_path(@project.namespace, @project, hook), class: "btn btn-sm btn-grouped" - else
= link_to 'Remove', namespace_project_hook_path(@project.namespace, @project, hook), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove btn-sm btn-grouped" %p.profile-settings-message.text-center.append-bottom-0
.monospace= hook.url No webhooks found, add one in the form above.
%div
- %w(push_events tag_push_events issues_events note_events merge_requests_events build_events).each do |trigger|
- if hook.send(trigger)
%span.label.label-gray= trigger.titleize
%span.label.label-gray SSL Verification: #{hook.enable_ssl_verification ? "enabled" : "disabled"}
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
.panel-body .panel-body
%pre %pre
:preserve :preserve
#{@project.import_error.try(:strip)} #{sanitize_repo_path(@project.import_error)}
= form_for @project, url: namespace_project_import_path(@project.namespace, @project), method: :post, html: { class: 'form-horizontal' } do |f| = form_for @project, url: namespace_project_import_path(@project.namespace, @project), method: :post, html: { class: 'form-horizontal' } do |f|
= render "shared/import_form", f: f = render "shared/import_form", f: f
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment