Commit 742e6eee authored by Vinnie Okada's avatar Vinnie Okada

Merge branch 'upstream-master' into markdown-preview

Conflicts:
	spec/routing/project_routing_spec.rb
parents 7a5072c5 bbf9953b
...@@ -39,3 +39,4 @@ public/assets/ ...@@ -39,3 +39,4 @@ public/assets/
.envrc .envrc
dump.rdb dump.rdb
tags tags
.gitlab_shell_secret
v 7.6.0
- Fork repository to groups
- New rugged version
- Add CRON=1 backup setting for quiet backups
- Fix failing wiki restore
-
- Add optional Sidekiq MemoryKiller middleware (enabled via SIDEKIQ_MAX_RSS env variable)
-
-
- Monokai highlighting style now more faithful to original design (Mark Riedesel)
- Create project with repository in synchrony
- Added ability to create empty repo or import existing one if project does not have repository
-
-
- Reactivate highlight.js language autodetection
- Mobile UI improvements
-
- Change maximum avatar file size from 100KB to 200KB
-
-
- In the docker directory is a container template based on the Omnibus packages.
- Update Sidekiq to version 2.17.8
- Add author filter to project issues and merge requests pages
- Atom feed for user activity
v 7.5.2
- Don't log Sidekiq arguments by default
v 7.5.0
- API: Add support for Hipchat (Kevin Houdebert)
- Add time zone configuration in gitlab.yml (Sullivan Senechal)
- Fix LDAP authentication for Git HTTP access
- Run 'GC.start' after every EmailsOnPushWorker job
- Fix LDAP config lookup for provider 'ldap'
- Drop all sequences during Postgres database restore
- Project title links to project homepage (Ben Bodenmiller)
- Add Atlassian Bamboo CI service (Drew Blessing)
- Mentioned @user will receive email even if he is not participating in issue or commit
- Session API: Use case-insensitive authentication like in UI (Andrey Krivko)
- Tie up loose ends with annotated tags: API & UI (Sean Edge)
- Return valid json for deleting branch via API (sponsored by O'Reilly Media)
- Expose username in project events API (sponsored by O'Reilly Media)
- Adds comments to commits in the API
- Performance improvements
- Fix post-receive issue for projects with deleted forks
- New gitlab-shell version with custom hooks support
- Improve code
- GitLab CI 5.2+ support (does not support older versions)
- Fixed bug when you can not push commits starting with 000000 to protected branches
- Added a password strength indicator
- Change project name and path in one form
- Display renamed files in diff views (Vinnie Okada)
- Fix raw view for public snippets
- Use secret token with GitLab internal API.
- Add missing timestamps to 'members' table
v 7.4.3
- Fix raw snippets view
- Fix security issue for member api
- Fix buildbox integration
v 7.4.2
- Fix internal snippet exposing for unauthenticated users
v 7.4.1
- Fix LDAP authentication for Git HTTP access
- Fix LDAP config lookup for provider 'ldap'
- Fix public snippets
- Fix 500 error on projects with nested submodules
v 7.4.0 v 7.4.0
- Refactored membership logic - Refactored membership logic
- Improve error reporting on users API (Julien Bianchi) - Improve error reporting on users API (Julien Bianchi)
...@@ -9,6 +79,7 @@ v 7.4.0 ...@@ -9,6 +79,7 @@ v 7.4.0
- Do not delete tmp/repositories itself during clean-up, only its contents - Do not delete tmp/repositories itself during clean-up, only its contents
- Support for backup uploads to remote storage - Support for backup uploads to remote storage
- Prevent notes polling when there are not notes - Prevent notes polling when there are not notes
- Internal ForkService: Prepare support for fork to a given namespace
- API: Add support for forking a project via the API (Bernhard Kaindl) - API: Add support for forking a project via the API (Bernhard Kaindl)
- API: filter project issues by milestone (Julien Bianchi) - API: filter project issues by milestone (Julien Bianchi)
- Fail harder in the backup script - Fail harder in the backup script
...@@ -28,6 +99,13 @@ v 7.4.0 ...@@ -28,6 +99,13 @@ v 7.4.0
- Improved repository graphs - Improved repository graphs
- Improve event note display in dashboard and project activity views (Vinnie Okada) - Improve event note display in dashboard and project activity views (Vinnie Okada)
- Add users sorting to admin area - Add users sorting to admin area
- UI improvements
- Fix ambiguous sha problem with mentioned commit
- Fixed bug with apostrophe when at mentioning users
- Add active directory ldap option
- Developers can push to wiki repo. Protected branches does not affect wiki repo any more
- Faster rev list
- Fix branch removal
v 7.3.2 v 7.3.2
- Fix creating new file via web editor - Fix creating new file via web editor
......
...@@ -54,6 +54,8 @@ We welcome merge requests with fixes and improvements to GitLab code, tests, and ...@@ -54,6 +54,8 @@ We welcome merge requests with fixes and improvements to GitLab code, tests, and
Merge requests can be filed either at [gitlab.com](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests) or [github.com](https://github.com/gitlabhq/gitlabhq/pulls). Merge requests can be filed either at [gitlab.com](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests) or [github.com](https://github.com/gitlabhq/gitlabhq/pulls).
If you are new to GitLab development (or web development in general), search for the label `easyfix` ([gitlab.com](https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=easyfix), [github](https://github.com/gitlabhq/gitlabhq/labels/easyfix)). Those are issues easy to fix, marked by the GitLab core-team. If you are unsure how to proceed but want to help, mention one of the core-team members to give you a hint.
### Merge request guidelines ### Merge request guidelines
If you can, please submit a merge request with the fix or improvements including tests. If you don't know how to fix the issue but can write a test that exposes the issue we will accept that as well. In general bug fixes that include a regression test are merged quickly while new features without proper tests are least likely to receive timely feedback. The workflow to make a merge request is as follows: If you can, please submit a merge request with the fix or improvements including tests. If you don't know how to fix the issue but can write a test that exposes the issue we will accept that as well. In general bug fixes that include a regression test are merged quickly while new features without proper tests are least likely to receive timely feedback. The workflow to make a merge request is as follows:
...@@ -78,15 +80,33 @@ The **official merge window** is in the beginning of the month from the 1st to t ...@@ -78,15 +80,33 @@ The **official merge window** is in the beginning of the month from the 1st to t
Please keep the change in a single MR **as small as possible**. If you want to contribute a large feature think very hard what the minimum viable change is. Can you split functionality? Can you only submit the backend/API code? Can you start with a very simple UI? Can you do part of the refactor? The increased reviewability of small MR's that leads to higher code quality is more important to us than having a minimal commit log. The smaller a MR is the more likely it is it will be merged (quickly), after that you can send more MR's to enhance it. Please keep the change in a single MR **as small as possible**. If you want to contribute a large feature think very hard what the minimum viable change is. Can you split functionality? Can you only submit the backend/API code? Can you start with a very simple UI? Can you do part of the refactor? The increased reviewability of small MR's that leads to higher code quality is more important to us than having a minimal commit log. The smaller a MR is the more likely it is it will be merged (quickly), after that you can send more MR's to enhance it.
For examples of feedback on merge requests please look at already [closed merge requests](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests?assignee_id=&label_name=&milestone_id=&scope=&sort=&state=closed). If you would like quick feedback on your merge request feel free to mention one of the Merge Marshalls of [the core-team](https://about.gitlab.com/core-team/). Please ensure that your merge request meets the following contribution acceptance criteria. For examples of feedback on merge requests please look at already [closed merge requests](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests?assignee_id=&label_name=&milestone_id=&scope=&sort=&state=closed). If you would like quick feedback on your merge request feel free to mention one of the Merge Marshalls of [the core-team](https://about.gitlab.com/core-team/). Please ensure that your merge request meets the contribution acceptance criteria.
## Definition of done
If you contribute to GitLab please know that changes involve more than just code.
We have the following [definition of done](http://guide.agilealliance.org/guide/definition-of-done.html).
Please ensure you support the feature you contribute through all of these steps.
1. Description explaning the relevancy (see following item)
1. Working and clean code that is commented where needed
1. Unit and integration tests that pass on the CI server
1. Documented in the /doc directory
1. Changelog entry added
1. Reviewed and any concerns are addressed
1. Merged by the project lead
1. Added to the release blog article
1. Added to [the website](https://gitlab.com/gitlab-com/www-gitlab-com/) if relevant
1. Community questions answered
1. Answers to questions radiated (in docs/wiki/etc.)
**Please format your merge request description as follows:** ## Merge request description format
1. What does this MR do? 1. What does this MR do?
1. Are there points in the code the reviewer needs to double check? 1. Are there points in the code the reviewer needs to double check?
1. Why was this MR needed? 1. Why was this MR needed?
1. What are the relevant issue numbers / [Feature requests](http://feedback.gitlab.com/)? 1. What are the relevant issue numbers / [Feature requests](http://feedback.gitlab.com/)?
1. Screenshots (If appropriate) 1. Screenshots (if relevant)
## Contribution acceptance criteria ## Contribution acceptance criteria
...@@ -101,7 +121,11 @@ For examples of feedback on merge requests please look at already [closed merge ...@@ -101,7 +121,11 @@ For examples of feedback on merge requests please look at already [closed merge
1. Contains functionality we think other users will benefit from too 1. Contains functionality we think other users will benefit from too
1. Doesn't add configuration options since they complicate future changes 1. Doesn't add configuration options since they complicate future changes
1. Changes after submitting the merge request should be in separate commits (no squashing). You will be asked to squash when the review is over, before merging. 1. Changes after submitting the merge request should be in separate commits (no squashing). You will be asked to squash when the review is over, before merging.
1. It conforms to the following style guides 1. It conforms to the following style guides.
If your change touches a line that does not follow the style,
modify the entire line to follow it. This prevents linting tools from generating warnings.
Don't touch neighbouring lines. As an exception, automatic mass refactoring modifications
may leave style non-compliant.
## Style guides ## Style guides
......
...@@ -31,13 +31,13 @@ gem 'omniauth-shibboleth' ...@@ -31,13 +31,13 @@ gem 'omniauth-shibboleth'
# Extracting information from a git repository # Extracting information from a git repository
# Provide access to Gitlab::Git library # Provide access to Gitlab::Git library
gem "gitlab_git", '7.0.0.rc9' gem "gitlab_git", '7.0.0.rc12'
# Ruby/Rack Git Smart-HTTP Server Handler # Ruby/Rack Git Smart-HTTP Server Handler
gem 'gitlab-grack', '~> 2.0.0.pre', require: 'grack' gem 'gitlab-grack', '~> 2.0.0.pre', require: 'grack'
# LDAP Auth # LDAP Auth
gem 'gitlab_omniauth-ldap', '1.1.0', require: "omniauth-ldap" gem 'gitlab_omniauth-ldap', '1.2.0', require: "omniauth-ldap"
# Git Wiki # Git Wiki
gem 'gollum-lib', '~> 3.0.0' gem 'gollum-lib', '~> 3.0.0'
...@@ -112,7 +112,7 @@ gem "acts-as-taggable-on" ...@@ -112,7 +112,7 @@ gem "acts-as-taggable-on"
# Background jobs # Background jobs
gem 'slim' gem 'slim'
gem 'sinatra', require: nil gem 'sinatra', require: nil
gem 'sidekiq', '2.17.0' gem 'sidekiq', '2.17.8'
# HTTP requests # HTTP requests
gem "httparty" gem "httparty"
...@@ -134,7 +134,7 @@ gem "redis-rails" ...@@ -134,7 +134,7 @@ gem "redis-rails"
gem 'tinder', '~> 1.9.2' gem 'tinder', '~> 1.9.2'
# HipChat integration # HipChat integration
gem "hipchat", "~> 0.14.0" gem "hipchat", "~> 1.4.0"
# Flowdock integration # Flowdock integration
gem "gitlab-flowdock-git-hook", "~> 0.4.2" gem "gitlab-flowdock-git-hook", "~> 0.4.2"
...@@ -143,7 +143,7 @@ gem "gitlab-flowdock-git-hook", "~> 0.4.2" ...@@ -143,7 +143,7 @@ gem "gitlab-flowdock-git-hook", "~> 0.4.2"
gem "gemnasium-gitlab-service", "~> 0.2" gem "gemnasium-gitlab-service", "~> 0.2"
# Slack integration # Slack integration
gem "slack-notifier", "~> 0.3.2" gem "slack-notifier", "~> 1.0.0"
# d3 # d3
gem "d3_rails", "~> 3.1.4" gem "d3_rails", "~> 3.1.4"
...@@ -186,6 +186,7 @@ gem "gon", '~> 5.0.0' ...@@ -186,6 +186,7 @@ gem "gon", '~> 5.0.0'
gem 'nprogress-rails' gem 'nprogress-rails'
gem 'request_store' gem 'request_store'
gem "virtus" gem "virtus"
gem 'addressable'
group :development do group :development do
gem "annotate", "~> 2.6.0.beta2" gem "annotate", "~> 2.6.0.beta2"
......
...@@ -78,7 +78,7 @@ GEM ...@@ -78,7 +78,7 @@ GEM
coffee-script-source (1.6.3) coffee-script-source (1.6.3)
colored (1.2) colored (1.2)
colorize (0.5.8) colorize (0.5.8)
connection_pool (1.2.0) connection_pool (2.1.0)
coveralls (0.7.0) coveralls (0.7.0)
multi_json (~> 1.3) multi_json (~> 1.3)
rest-client rest-client
...@@ -179,17 +179,17 @@ GEM ...@@ -179,17 +179,17 @@ GEM
mime-types (~> 1.19) mime-types (~> 1.19)
gitlab_emoji (0.0.1.1) gitlab_emoji (0.0.1.1)
emoji (~> 1.0.1) emoji (~> 1.0.1)
gitlab_git (7.0.0.rc9) gitlab_git (7.0.0.rc12)
activesupport (~> 4.0) activesupport (~> 4.0)
charlock_holmes (~> 0.6) charlock_holmes (~> 0.6)
gitlab-linguist (~> 3.0) gitlab-linguist (~> 3.0)
rugged (~> 0.21.0) rugged (~> 0.21.2)
gitlab_meta (7.0) gitlab_meta (7.0)
gitlab_omniauth-ldap (1.1.0) gitlab_omniauth-ldap (1.2.0)
net-ldap (~> 0.7.0) net-ldap (~> 0.9)
omniauth (~> 1.0) omniauth (~> 1.0)
pyu-ruby-sasl (~> 0.0.3.1) pyu-ruby-sasl (~> 0.0.3.1)
rubyntlm (~> 0.1.1) rubyntlm (~> 0.3)
gollum-lib (3.0.0) gollum-lib (3.0.0)
github-markup (~> 1.1.0) github-markup (~> 1.1.0)
gitlab-grit (~> 2.6.5) gitlab-grit (~> 2.6.5)
...@@ -235,8 +235,7 @@ GEM ...@@ -235,8 +235,7 @@ GEM
railties (>= 4.0.1) railties (>= 4.0.1)
hashie (2.1.2) hashie (2.1.2)
hike (1.2.3) hike (1.2.3)
hipchat (0.14.0) hipchat (1.4.0)
httparty
httparty httparty
html-pipeline (1.11.0) html-pipeline (1.11.0)
activesupport (>= 2) activesupport (>= 2)
...@@ -281,7 +280,7 @@ GEM ...@@ -281,7 +280,7 @@ GEM
addressable (~> 2.3) addressable (~> 2.3)
letter_opener (1.1.2) letter_opener (1.1.2)
launchy (~> 2.2) launchy (~> 2.2)
libv8 (3.16.14.3) libv8 (3.16.14.7)
listen (2.3.1) listen (2.3.1)
celluloid (>= 0.15.2) celluloid (>= 0.15.2)
rb-fsevent (>= 0.9.3) rb-fsevent (>= 0.9.3)
...@@ -299,7 +298,7 @@ GEM ...@@ -299,7 +298,7 @@ GEM
multi_xml (0.5.5) multi_xml (0.5.5)
multipart-post (1.2.0) multipart-post (1.2.0)
mysql2 (0.3.16) mysql2 (0.3.16)
net-ldap (0.7.0) net-ldap (0.9.0)
net-scp (1.1.2) net-scp (1.1.2)
net-ssh (>= 2.6.5) net-ssh (>= 2.6.5)
net-ssh (2.8.0) net-ssh (2.8.0)
...@@ -403,7 +402,7 @@ GEM ...@@ -403,7 +402,7 @@ GEM
rdoc (3.12.2) rdoc (3.12.2)
json (~> 1.4) json (~> 1.4)
redcarpet (3.1.2) redcarpet (3.1.2)
redis (3.0.6) redis (3.1.0)
redis-actionpack (4.0.0) redis-actionpack (4.0.0)
actionpack (~> 4) actionpack (~> 4)
redis-rack (~> 1.5.0) redis-rack (~> 1.5.0)
...@@ -411,8 +410,8 @@ GEM ...@@ -411,8 +410,8 @@ GEM
redis-activesupport (4.0.0) redis-activesupport (4.0.0)
activesupport (~> 4) activesupport (~> 4)
redis-store (~> 1.1.0) redis-store (~> 1.1.0)
redis-namespace (1.4.1) redis-namespace (1.5.1)
redis (~> 3.0.4) redis (~> 3.0, >= 3.0.4)
redis-rack (1.5.0) redis-rack (1.5.0)
rack (~> 1.5) rack (~> 1.5)
redis-store (~> 1.1.0) redis-store (~> 1.1.0)
...@@ -445,9 +444,9 @@ GEM ...@@ -445,9 +444,9 @@ GEM
rspec-expectations (~> 2.14.0) rspec-expectations (~> 2.14.0)
rspec-mocks (~> 2.14.0) rspec-mocks (~> 2.14.0)
ruby-progressbar (1.2.0) ruby-progressbar (1.2.0)
rubyntlm (0.1.1) rubyntlm (0.4.0)
rubypants (0.2.0) rubypants (0.2.0)
rugged (0.21.0) rugged (0.21.2)
safe_yaml (0.9.7) safe_yaml (0.9.7)
sanitize (2.1.0) sanitize (2.1.0)
nokogiri (>= 1.4.4) nokogiri (>= 1.4.4)
...@@ -471,12 +470,12 @@ GEM ...@@ -471,12 +470,12 @@ GEM
sexp_processor (4.4.0) sexp_processor (4.4.0)
shoulda-matchers (2.1.0) shoulda-matchers (2.1.0)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
sidekiq (2.17.0) sidekiq (2.17.8)
celluloid (>= 0.15.2) celluloid (= 0.15.2)
connection_pool (>= 1.0.0) connection_pool (~> 2.0)
json json
redis (>= 3.0.4) redis (~> 3.1)
redis-namespace (>= 1.3.1) redis-namespace (~> 1.3)
simple_oauth (0.1.9) simple_oauth (0.1.9)
simplecov (0.9.0) simplecov (0.9.0)
docile (~> 1.1.0) docile (~> 1.1.0)
...@@ -488,7 +487,7 @@ GEM ...@@ -488,7 +487,7 @@ GEM
rack-protection (~> 1.4) rack-protection (~> 1.4)
tilt (~> 1.3, >= 1.3.4) tilt (~> 1.3, >= 1.3.4)
six (0.2.0) six (0.2.0)
slack-notifier (0.3.2) slack-notifier (1.0.0)
slim (2.0.2) slim (2.0.2)
temple (~> 0.6.6) temple (~> 0.6.6)
tilt (>= 1.3.3, < 2.1) tilt (>= 1.3.3, < 2.1)
...@@ -592,6 +591,7 @@ DEPENDENCIES ...@@ -592,6 +591,7 @@ DEPENDENCIES
RedCloth RedCloth
ace-rails-ap ace-rails-ap
acts-as-taggable-on acts-as-taggable-on
addressable
annotate (~> 2.6.0.beta2) annotate (~> 2.6.0.beta2)
asciidoctor (= 0.1.4) asciidoctor (= 0.1.4)
awesome_print awesome_print
...@@ -624,9 +624,9 @@ DEPENDENCIES ...@@ -624,9 +624,9 @@ DEPENDENCIES
gitlab-grack (~> 2.0.0.pre) gitlab-grack (~> 2.0.0.pre)
gitlab-linguist (~> 3.0.0) gitlab-linguist (~> 3.0.0)
gitlab_emoji (~> 0.0.1.1) gitlab_emoji (~> 0.0.1.1)
gitlab_git (= 7.0.0.rc9) gitlab_git (= 7.0.0.rc12)
gitlab_meta (= 7.0) gitlab_meta (= 7.0)
gitlab_omniauth-ldap (= 1.1.0) gitlab_omniauth-ldap (= 1.2.0)
gollum-lib (~> 3.0.0) gollum-lib (~> 3.0.0)
gon (~> 5.0.0) gon (~> 5.0.0)
grape (~> 0.6.1) grape (~> 0.6.1)
...@@ -635,7 +635,7 @@ DEPENDENCIES ...@@ -635,7 +635,7 @@ DEPENDENCIES
guard-rspec guard-rspec
guard-spinach guard-spinach
haml-rails haml-rails
hipchat (~> 0.14.0) hipchat (~> 1.4.0)
html-pipeline-gitlab (~> 0.1.0) html-pipeline-gitlab (~> 0.1.0)
httparty httparty
jasmine (= 2.0.2) jasmine (= 2.0.2)
...@@ -684,11 +684,11 @@ DEPENDENCIES ...@@ -684,11 +684,11 @@ DEPENDENCIES
semantic-ui-sass (~> 0.16.1.0) semantic-ui-sass (~> 0.16.1.0)
settingslogic settingslogic
shoulda-matchers (~> 2.1.0) shoulda-matchers (~> 2.1.0)
sidekiq (= 2.17.0) sidekiq (= 2.17.8)
simplecov simplecov
sinatra sinatra
six six
slack-notifier (~> 0.3.2) slack-notifier (~> 1.0.0)
slim slim
spinach-rails spinach-rails
spring (= 1.1.3) spring (= 1.1.3)
......
...@@ -55,14 +55,8 @@ Since a manual installation is a lot of work and error prone we strongly recomme ...@@ -55,14 +55,8 @@ Since a manual installation is a lot of work and error prone we strongly recomme
## Third-party applications ## Third-party applications
Access GitLab from multiple platforms with applications below. There are a lot of applications and API wrappers for GitLab.
These applications are maintained by contributors, GitLab B.V. does not offer support for them. Find them [on our website](https://about.gitlab.com/applications/).
- [iPhone app](http://gitlabcontrol.com/)
- [Android app](https://play.google.com/store/apps/details?id=com.bd.gitlab&hl=en)
- [Chrome app](https://chrome.google.com/webstore/detail/chrome-gitlab-notifier/eageapgbnjicdjjihgclpclilenjbobi)
- [Command line client](https://github.com/drewblessing/gitlab-cli)
- [Ruby API wrapper](https://github.com/NARKOZ/gitlab)
### New versions ### New versions
...@@ -137,4 +131,4 @@ Please see [Getting help for GitLab](https://about.gitlab.com/getting-help/) on ...@@ -137,4 +131,4 @@ Please see [Getting help for GitLab](https://about.gitlab.com/getting-help/) on
## Is it awesome? ## Is it awesome?
Thanks for [asking this question](https://twitter.com/supersloth/status/489462789384056832) Joshua. Thanks for [asking this question](https://twitter.com/supersloth/status/489462789384056832) Joshua.
[These people](https://twitter.com/gitlabhq/favorites) seem to like it. [These people](https://twitter.com/gitlab/favorites) seem to like it.
7.4.0-pre 7.6.0.pre
class Activities class @Activities
constructor: -> constructor: ->
Pager.init 20, true Pager.init 20, true
$(".event_filter_link").bind "click", (event) => $(".event_filter_link").bind "click", (event) =>
...@@ -27,5 +27,3 @@ class Activities ...@@ -27,5 +27,3 @@ class Activities
event_filters.splice index, 1 event_filters.splice index, 1
$.cookie "event_filter", event_filters.join(","), { path: '/' } $.cookie "event_filter", event_filters.join(","), { path: '/' }
@Activities = Activities
class Admin class @Admin
constructor: -> constructor: ->
$('input#user_force_random_password').on 'change', (elem) -> $('input#user_force_random_password').on 'change', (elem) ->
elems = $('#user_password, #user_password_confirmation') elems = $('#user_password, #user_password_confirmation')
...@@ -51,5 +51,3 @@ class Admin ...@@ -51,5 +51,3 @@ class Admin
$('li.group_member').bind 'ajax:success', -> $('li.group_member').bind 'ajax:success', ->
Turbolinks.visit(location.href) Turbolinks.visit(location.href)
@Admin = Admin
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#= require jquery.turbolinks #= require jquery.turbolinks
#= require turbolinks #= require turbolinks
#= require bootstrap #= require bootstrap
#= require password_strength
#= require select2 #= require select2
#= require raphael #= require raphael
#= require g.raphael-min #= require g.raphael-min
...@@ -63,7 +64,7 @@ window.extractLast = (term) -> ...@@ -63,7 +64,7 @@ window.extractLast = (term) ->
return split( term ).pop() return split( term ).pop()
window.rstrip = (val) -> window.rstrip = (val) ->
return val.replace(/\s+$/, '') return if val then val.replace(/\s+$/, '') else val
# Disable button if text field is empty # Disable button if text field is empty
window.disableButtonIfEmptyField = (field_selector, button_selector) -> window.disableButtonIfEmptyField = (field_selector, button_selector) ->
......
class BlobView class @BlobView
constructor: -> constructor: ->
# handle multi-line select # handle multi-line select
handleMultiSelect = (e) -> handleMultiSelect = (e) ->
...@@ -71,6 +71,3 @@ class BlobView ...@@ -71,6 +71,3 @@ class BlobView
# Highlight the correct lines when the hash part of the URL changes # Highlight the correct lines when the hash part of the URL changes
$(window).on("hashchange", highlightBlobLines) $(window).on("hashchange", highlightBlobLines)
@BlobView = BlobView
class Commit class @Commit
constructor: -> constructor: ->
$('.files .diff-file').each -> $('.files .diff-file').each ->
new CommitFile(this) new CommitFile(this)
@Commit = Commit
class CommitFile class @CommitFile
constructor: (file) -> constructor: (file) ->
if $('.image', file).length if $('.image', file).length
new ImageFile(file) new ImageFile(file)
@CommitFile = CommitFile
class ImageFile class @ImageFile
# Width where images must fits in, for 2-up this gets divided by 2 # Width where images must fits in, for 2-up this gets divided by 2
@availWidth = 900 @availWidth = 900
...@@ -124,5 +124,3 @@ class ImageFile ...@@ -124,5 +124,3 @@ class ImageFile
else else
img.on 'load', => img.on 'load', =>
callback.call(this, domImg.naturalWidth, domImg.naturalHeight) callback.call(this, domImg.naturalWidth, domImg.naturalHeight)
@ImageFile = ImageFile
class CommitsList class @CommitsList
@data = @data =
ref: null ref: null
limit: 0 limit: 0
...@@ -53,5 +53,3 @@ class CommitsList ...@@ -53,5 +53,3 @@ class CommitsList
@disable @disable
callback: => callback: =>
this.getOld() this.getOld()
this.CommitsList = CommitsList
class ConfirmDangerModal class @ConfirmDangerModal
constructor: (form, text) -> constructor: (form, text) ->
@form = form @form = form
$('.js-confirm-text').text(text || '') $('.js-confirm-text').text(text || '')
...@@ -16,5 +16,3 @@ class ConfirmDangerModal ...@@ -16,5 +16,3 @@ class ConfirmDangerModal
$('.js-confirm-danger-submit').on 'click', => $('.js-confirm-danger-submit').on 'click', =>
@form.submit() @form.submit()
@ConfirmDangerModal = ConfirmDangerModal
class Dashboard class @Dashboard
constructor: -> constructor: ->
@initSidebarTab() @initSidebarTab()
...@@ -28,6 +28,3 @@ class Dashboard ...@@ -28,6 +28,3 @@ class Dashboard
# show tab from cookie # show tab from cookie
sidebar_filter = $.cookie(key) sidebar_filter = $.cookie(key)
$("#" + sidebar_filter).tab('show') if sidebar_filter $("#" + sidebar_filter).tab('show') if sidebar_filter
@Dashboard = Dashboard
class Diff class @Diff
UNFOLD_COUNT = 20 UNFOLD_COUNT = 20
constructor: -> constructor: ->
$(document).on('click', '.js-unfold', (event) => $(document).on('click', '.js-unfold', (event) =>
...@@ -41,6 +41,3 @@ class Diff ...@@ -41,6 +41,3 @@ class Diff
lines = line.children().slice(0, 2) lines = line.children().slice(0, 2)
line_numbers = ($(l).attr('data-linenumber') for l in lines) line_numbers = ($(l).attr('data-linenumber') for l in lines)
(parseInt(line_number) for line_number in line_numbers) (parseInt(line_number) for line_number in line_numbers)
@Diff = Diff
...@@ -58,15 +58,11 @@ class Dispatcher ...@@ -58,15 +58,11 @@ class Dispatcher
when 'groups:show', 'projects:show' when 'groups:show', 'projects:show'
new Activities() new Activities()
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
when 'projects:new'
new Project()
when 'projects:edit'
new Project()
shortcut_handler = new ShortcutsNavigation()
when 'projects:teams:members:index'
new TeamMembers()
when 'groups:members' when 'groups:members'
new GroupMembers() new GroupMembers()
new UsersSelect()
when 'groups:new', 'groups:edit', 'admin:groups:edit'
new GroupAvatar()
when 'projects:tree:show' when 'projects:tree:show'
new TreeView() new TreeView()
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
...@@ -79,13 +75,35 @@ class Dispatcher ...@@ -79,13 +75,35 @@ class Dispatcher
# Ensure we don't create a particular shortcut handler here. This is # Ensure we don't create a particular shortcut handler here. This is
# already created, where the network graph is created. # already created, where the network graph is created.
shortcut_handler = true shortcut_handler = true
when 'projects:forks:new'
new ProjectFork()
when 'users:show'
new User()
switch path.first() switch path.first()
when 'admin' then new Admin() when 'admin'
new Admin()
switch path[1]
when 'groups'
new UsersSelect()
when 'projects'
new NamespaceSelect()
when 'dashboard' when 'dashboard'
shortcut_handler = new ShortcutsDashboardNavigation() shortcut_handler = new ShortcutsDashboardNavigation()
when 'profiles'
new Profile()
when 'projects' when 'projects'
new Project()
switch path[1] switch path[1]
when 'edit'
shortcut_handler = new ShortcutsNavigation()
new ProjectNew()
when 'new'
new ProjectNew()
when 'show'
new ProjectShow()
when 'issues', 'merge_requests'
new ProjectUsersSelect()
when 'wikis' when 'wikis'
new Wikis() new Wikis()
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
...@@ -94,6 +112,7 @@ class Dispatcher ...@@ -94,6 +112,7 @@ class Dispatcher
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
when 'team_members', 'deploy_keys', 'hooks', 'services', 'protected_branches' when 'team_members', 'deploy_keys', 'hooks', 'services', 'protected_branches'
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
new UsersSelect()
# If we haven't installed a custom shortcut handler, install the default one # If we haven't installed a custom shortcut handler, install the default one
......
class Flash class @Flash
constructor: (message, type)-> constructor: (message, type)->
flash = $(".flash-container") flash = $(".flash-container")
flash.html("") flash.html("")
...@@ -10,5 +10,3 @@ class Flash ...@@ -10,5 +10,3 @@ class Flash
flash.click -> $(@).fadeOut() flash.click -> $(@).fadeOut()
flash.show() flash.show()
@Flash = Flash
class @GroupAvatar
constructor: ->
$('.js-choose-group-avatar-button').bind "click", ->
form = $(this).closest("form")
form.find(".js-group-avatar-input").click()
$('.js-group-avatar-input').bind "change", ->
form = $(this).closest("form")
filename = $(this).val().replace(/^.*[\\\/]/, '')
form.find(".js-avatar-filename").text(filename)
class GroupMembers class @GroupMembers
constructor: -> constructor: ->
$('li.group_member').bind 'ajax:success', -> $('li.group_member').bind 'ajax:success', ->
$(this).fadeOut() $(this).fadeOut()
@GroupMembers = GroupMembers
$ ->
# avatar
$('.js-choose-group-avatar-button').bind "click", ->
form = $(this).closest("form")
form.find(".js-group-avatar-input").click()
$('.js-group-avatar-input').bind "change", ->
form = $(this).closest("form")
filename = $(this).val().replace(/^.*[\\\/]/, '')
form.find(".js-avatar-filename").text(filename)
class Issue class @Issue
constructor: -> constructor: ->
$('.edit-issue.inline-update input[type="submit"]').hide() $('.edit-issue.inline-update input[type="submit"]').hide()
$(".issue-box .inline-update").on "change", "select", -> $(".issue-box .inline-update").on "change", "select", ->
...@@ -15,5 +15,3 @@ class Issue ...@@ -15,5 +15,3 @@ class Issue
"issue" "issue"
updateTaskState updateTaskState
) )
@Issue = Issue
class Labels class @Labels
constructor: -> constructor: ->
form = $('.label-form') form = $('.label-form')
@setupLabelForm(form) @setupLabelForm(form)
...@@ -31,5 +31,3 @@ class Labels ...@@ -31,5 +31,3 @@ class Labels
# Notify the form, that color has changed # Notify the form, that color has changed
$('.label-form').trigger('keyup') $('.label-form').trigger('keyup')
e.preventDefault() e.preventDefault()
@Labels = Labels
class MergeRequest class @MergeRequest
constructor: (@opts) -> constructor: (@opts) ->
@initContextWidget() @initContextWidget()
this.$el = $('.merge-request') this.$el = $('.merge-request')
...@@ -132,5 +132,3 @@ class MergeRequest ...@@ -132,5 +132,3 @@ class MergeRequest
this.$('.automerge_widget').hide() this.$('.automerge_widget').hide()
this.$('.merge-in-progress').hide() this.$('.merge-in-progress').hide()
this.$('.automerge_widget.already_cannot_be_merged').show() this.$('.automerge_widget.already_cannot_be_merged').show()
this.MergeRequest = MergeRequest
class Milestone class @Milestone
@updateIssue: (li, issue_url, data) -> @updateIssue: (li, issue_url, data) ->
$.ajax $.ajax
type: "PUT" type: "PUT"
...@@ -115,5 +115,3 @@ class Milestone ...@@ -115,5 +115,3 @@ class Milestone
Milestone.updateMergeRequest(ui.item, merge_request_url, data) Milestone.updateMergeRequest(ui.item, merge_request_url, data)
).disableSelection() ).disableSelection()
@Milestone = Milestone
$ -> class @NamespaceSelect
constructor: ->
namespaceFormatResult = (namespace) -> namespaceFormatResult = (namespace) ->
markup = "<div class='namespace-result'>" markup = "<div class='namespace-result'>"
markup += "<span class='namespace-kind'>" + namespace.kind + "</span>" markup += "<span class='namespace-kind'>" + namespace.kind + "</span>"
......
class Notes class @Notes
@interval: null @interval: null
constructor: (notes_url, note_ids, last_fetched_at) -> constructor: (notes_url, note_ids, last_fetched_at) ->
...@@ -465,7 +465,3 @@ class Notes ...@@ -465,7 +465,3 @@ class Notes
else else
form.find('.js-note-target-reopen').text('Reopen') form.find('.js-note-target-reopen').text('Reopen')
form.find('.js-note-target-close').text('Close') form.find('.js-note-target-close').text('Close')
@Notes = Notes
class NotesVotes class @NotesVotes
updateVotes: -> updateVotes: ->
votes = $("#votes .votes") votes = $("#votes .votes")
notes = $("#notes-list .note .vote") notes = $("#notes-list .note .vote")
...@@ -18,5 +18,3 @@ class NotesVotes ...@@ -18,5 +18,3 @@ class NotesVotes
# replace vote numbers # replace vote numbers
votes.find(".upvotes").text votes.find(".upvotes").text().replace(/\d+/, upvotes) votes.find(".upvotes").text votes.find(".upvotes").text().replace(/\d+/, upvotes)
votes.find(".downvotes").text votes.find(".downvotes").text().replace(/\d+/, downvotes) votes.find(".downvotes").text votes.find(".downvotes").text().replace(/\d+/, downvotes)
@NotesVotes = NotesVotes
#= require pwstrength-bootstrap-1.2.2
overwritten_messages =
wordSimilarToUsername: "Your password should not contain your username"
overwritten_rules =
wordSequences: false
options =
showProgressBar: false
showVerdicts: false
showPopover: true
showErrors: true
showStatus: true
errorMessages: overwritten_messages
$(document).ready ->
profileOptions = {}
profileOptions.ui = options
profileOptions.rules =
activated: overwritten_rules
deviseOptions = {}
deviseOptions.common =
usernameField: "#user_username"
deviseOptions.ui = options
deviseOptions.rules =
activated: overwritten_rules
$("#user_password_profile").pwstrength profileOptions
$("#user_password_sign_up").pwstrength deviseOptions
$("#user_password_recover").pwstrength deviseOptions
$ -> class @Profile
constructor: ->
$('.edit_user .application-theme input, .edit_user .code-preview-theme input').click -> $('.edit_user .application-theme input, .edit_user .code-preview-theme input').click ->
# Submit the form # Submit the form
$('.edit_user').submit() $('.edit_user').submit()
...@@ -26,5 +27,3 @@ $ -> ...@@ -26,5 +27,3 @@ $ ->
form = $(this).closest("form") form = $(this).closest("form")
filename = $(this).val().replace(/^.*[\\\/]/, '') filename = $(this).val().replace(/^.*[\\\/]/, '')
form.find(".js-avatar-filename").text(filename) form.find(".js-avatar-filename").text(filename)
$('.profile-groups-avatars').tooltip("placement": "top")
class Project class @Project
constructor: -> constructor: ->
$('.project-edit-container').on 'ajax:before', =>
$('.project-edit-container').hide()
$('.save-project-loader').show()
@initEvents()
initEvents: ->
disableButtonIfEmptyField '#project_name', '.project-submit'
$('#project_issues_enabled').change ->
if ($(this).is(':checked') == true)
$('#project_issues_tracker').removeAttr('disabled')
else
$('#project_issues_tracker').attr('disabled', 'disabled')
$('#project_issues_tracker').change()
$('#project_issues_tracker').change ->
if ($(this).val() == gon.default_issues_tracker || $(this).is(':disabled'))
$('#project_issues_tracker_id').attr('disabled', 'disabled')
else
$('#project_issues_tracker_id').removeAttr('disabled')
@Project = Project
$ ->
# Git clone panel switcher # Git clone panel switcher
scope = $ '.git-clone-holder' scope = $ '.git-clone-holder'
if scope.length > 0 if scope.length > 0
...@@ -46,17 +18,3 @@ $ -> ...@@ -46,17 +18,3 @@ $ ->
$.cookie('hide_no_ssh_message', 'false', { path: path }) $.cookie('hide_no_ssh_message', 'false', { path: path })
$(@).parents('.no-ssh-key-message').hide() $(@).parents('.no-ssh-key-message').hide()
e.preventDefault() e.preventDefault()
$('.project-home-panel .star').on 'ajax:success', (e, data, status, xhr) ->
$(@).toggleClass('on').find('.count').html(data.star_count)
.on 'ajax:error', (e, xhr, status, error) ->
new Flash('Star toggle failed. Try again later.', 'alert')
$("a[data-toggle='tab']").on "shown.bs.tab", (e) ->
$.cookie "default_view", $(e.target).attr("href")
defaultView = $.cookie("default_view")
if defaultView
$("a[href=" + defaultView + "]").tab "show"
else
$("a[data-toggle='tab']:first").tab "show"
class @ProjectFork
constructor: ->
$('.fork-thumbnail a').on 'click', ->
$('.fork-namespaces').hide()
$('.save-project-loader').show()
class ProjectImport class @ProjectImport
constructor: -> constructor: ->
setTimeout -> setTimeout ->
Turbolinks.visit(location.href) Turbolinks.visit(location.href)
, 5000 , 5000
@ProjectImport = ProjectImport
class @ProjectNew
constructor: ->
$('.project-edit-container').on 'ajax:before', =>
$('.project-edit-container').hide()
$('.save-project-loader').show()
@initEvents()
initEvents: ->
disableButtonIfEmptyField '#project_name', '.project-submit'
$('#project_issues_enabled').change ->
if ($(this).is(':checked') == true)
$('#project_issues_tracker').removeAttr('disabled')
else
$('#project_issues_tracker').attr('disabled', 'disabled')
$('#project_issues_tracker').change()
$('#project_issues_tracker').change ->
if ($(this).val() == gon.default_issues_tracker || $(this).is(':disabled'))
$('#project_issues_tracker_id').attr('disabled', 'disabled')
else
$('#project_issues_tracker_id').removeAttr('disabled')
class @ProjectShow
constructor: ->
$('.project-home-panel .star').on 'ajax:success', (e, data, status, xhr) ->
$(@).toggleClass('on').find('.count').html(data.star_count)
.on 'ajax:error', (e, xhr, status, error) ->
new Flash('Star toggle failed. Try again later.', 'alert')
$("a[data-toggle='tab']").on "shown.bs.tab", (e) ->
$.cookie "default_view", $(e.target).attr("href")
defaultView = $.cookie("default_view")
if defaultView
$("a[href=" + defaultView + "]").tab "show"
else
$("a[data-toggle='tab']:first").tab "show"
@projectUsersSelect = class @ProjectUsersSelect
init: -> constructor: ->
$('.ajax-project-users-select').each (i, select) -> $('.ajax-project-users-select').each (i, select) =>
project_id = $(select).data('project-id') || $('body').data('project-id') project_id = $(select).data('project-id') || $('body').data('project-id')
$(select).select2 $(select).select2
...@@ -28,14 +28,16 @@ ...@@ -28,14 +28,16 @@
Api.user(id, callback) Api.user(id, callback)
formatResult: projectUsersSelect.projectUserFormatResult formatResult: (args...) =>
formatSelection: projectUsersSelect.projectUserFormatSelection @formatResult(args...)
formatSelection: (args...) =>
@formatSelection(args...)
dropdownCssClass: "ajax-project-users-dropdown" dropdownCssClass: "ajax-project-users-dropdown"
dropdownAutoWidth: true dropdownAutoWidth: true
escapeMarkup: (m) -> # we do not want to escape markup since we are displaying html in results escapeMarkup: (m) -> # we do not want to escape markup since we are displaying html in results
m m
projectUserFormatResult: (user) -> formatResult: (user) ->
if user.avatar_url if user.avatar_url
avatar = user.avatar_url avatar = user.avatar_url
else else
...@@ -52,8 +54,5 @@ ...@@ -52,8 +54,5 @@
<div class='user-username'>#{user.username}</div> <div class='user-username'>#{user.username}</div>
</div>" </div>"
projectUserFormatSelection: (user) -> formatSelection: (user) ->
user.name user.name
$ ->
projectUsersSelect.init()
class SearchAutocomplete class @SearchAutocomplete
constructor: (search_autocomplete_path, project_id, project_ref) -> constructor: (search_autocomplete_path, project_id, project_ref) ->
project_id = '' unless project_id project_id = '' unless project_id
project_ref = '' unless project_ref project_ref = '' unless project_ref
...@@ -9,5 +9,3 @@ class SearchAutocomplete ...@@ -9,5 +9,3 @@ class SearchAutocomplete
minLength: 1 minLength: 1
select: (event, ui) -> select: (event, ui) ->
location.href = ui.item.url location.href = ui.item.url
@SearchAutocomplete = SearchAutocomplete
class window.StatGraph class @StatGraph
@log: {} @log: {}
@get_log: -> @get_log: ->
@log @log
......
class window.ContributorsStatGraph class @ContributorsStatGraph
init: (log) -> init: (log) ->
@parsed_log = ContributorsStatGraphUtil.parse_log(log) @parsed_log = ContributorsStatGraphUtil.parse_log(log)
@set_current_field("commits") @set_current_field("commits")
......
class window.ContributorsGraph class @ContributorsGraph
MARGIN: MARGIN:
top: 20 top: 20
right: 20 right: 20
...@@ -44,7 +44,7 @@ class window.ContributorsGraph ...@@ -44,7 +44,7 @@ class window.ContributorsGraph
set_data: (data) -> set_data: (data) ->
@data = data @data = data
class window.ContributorsMasterGraph extends ContributorsGraph class @ContributorsMasterGraph extends ContributorsGraph
constructor: (@data) -> constructor: (@data) ->
@width = $('.container').width() - 70 @width = $('.container').width() - 70
@height = 200 @height = 200
...@@ -117,7 +117,7 @@ class window.ContributorsMasterGraph extends ContributorsGraph ...@@ -117,7 +117,7 @@ class window.ContributorsMasterGraph extends ContributorsGraph
@svg.select("path").attr("d", @area) @svg.select("path").attr("d", @area)
@svg.select(".y.axis").call(@y_axis) @svg.select(".y.axis").call(@y_axis)
class window.ContributorsAuthorGraph extends ContributorsGraph class @ContributorsAuthorGraph extends ContributorsGraph
constructor: (@data) -> constructor: (@data) ->
@width = $('.container').width()/2 - 100 @width = $('.container').width()/2 - 100
@height = 200 @height = 200
......
class TeamMembers
constructor: ->
$('.team-members .project-access-select').on "change", ->
$(this.form).submit()
@TeamMembers = TeamMembers
class TreeView class @TreeView
constructor: -> constructor: ->
@initKeyNav() @initKeyNav()
...@@ -39,5 +39,3 @@ class TreeView ...@@ -39,5 +39,3 @@ class TreeView
else if e.which is 13 else if e.which is 13
path = $('.tree-item.selected .tree-item-file-name a').attr('href') path = $('.tree-item.selected .tree-item-file-name a').attr('href')
Turbolinks.visit(path) Turbolinks.visit(path)
@TreeView = TreeView
class @User
constructor: ->
$('.profile-groups-avatars').tooltip("placement": "top")
$ -> class @UsersSelect
userFormatResult = (user) -> constructor: ->
if user.avatar_url $('.ajax-users-select').each (i, select) =>
avatar = user.avatar_url
else
avatar = gon.default_avatar_url
"<div class='user-result'>
<div class='user-image'><img class='avatar s24' src='#{avatar}'></div>
<div class='user-name'>#{user.name}</div>
<div class='user-username'>#{user.username}</div>
</div>"
userFormatSelection = (user) ->
user.name
$('.ajax-users-select').each (i, select) ->
$(select).select2 $(select).select2
placeholder: "Search for a user" placeholder: "Search for a user"
multiple: $(select).hasClass('multiselect') multiple: $(select).hasClass('multiselect')
...@@ -30,8 +16,25 @@ $ -> ...@@ -30,8 +16,25 @@ $ ->
Api.user(id, callback) Api.user(id, callback)
formatResult: userFormatResult formatResult: (args...) =>
formatSelection: userFormatSelection @formatResult(args...)
formatSelection: (args...) =>
@formatSelection(args...)
dropdownCssClass: "ajax-users-dropdown" dropdownCssClass: "ajax-users-dropdown"
escapeMarkup: (m) -> # we do not want to escape markup since we are displaying html in results escapeMarkup: (m) -> # we do not want to escape markup since we are displaying html in results
m m
formatResult: (user) ->
if user.avatar_url
avatar = user.avatar_url
else
avatar = gon.default_avatar_url
"<div class='user-result'>
<div class='user-image'><img class='avatar s24' src='#{avatar}'></div>
<div class='user-name'>#{user.name}</div>
<div class='user-username'>#{user.username}</div>
</div>"
formatSelection: (user) ->
user.name
class Wikis class @Wikis
constructor: -> constructor: ->
$('.build-new-wiki').bind "click", -> $('.build-new-wiki').bind "click", ->
field = $('#new_wiki_path') field = $('#new_wiki_path')
...@@ -7,6 +7,3 @@ class Wikis ...@@ -7,6 +7,3 @@ class Wikis
if(slug.length > 0) if(slug.length > 0)
location.href = path + "/" + slug location.href = path + "/" + slug
@Wikis = Wikis
// Details // Details
//-------- //--------
.js-details-container .content { display: none; } .js-details-container {
.js-details-container .content.hide { display: block; } .content {
.js-details-container.open .content { display: block; } display: none;
.js-details-container.open .content.hide { display: none; } &.hide { display: block; }
}
&.open .content {
display: block;
&.hide { display: none; }
}
}
// Toggle between two states. // Toggle between two states.
.js-toggler-container .turn-on { display: block; } .js-toggler-container {
.js-toggler-container .turn-off { display: none; } .turn-on { display: block; }
.js-toggler-container.on .turn-on { display: none; } .turn-off { display: none; }
.js-toggler-container.on .turn-off { display: block; } &.on {
.turn-on { display: none; }
.turn-off { display: block; }
}
}
...@@ -330,10 +330,6 @@ table { ...@@ -330,10 +330,6 @@ table {
} }
} }
@media (max-width: $screen-xs-max) {
.container .content { margin-top: 20px; }
}
.wiki .highlight, .note-body .highlight { .wiki .highlight, .note-body .highlight {
margin-bottom: 9px; margin-bottom: 9px;
} }
......
...@@ -42,7 +42,6 @@ ...@@ -42,7 +42,6 @@
} }
.file-content { .file-content {
background: #fff; background: #fff;
font-size: 11px;
&.image_file { &.image_file {
background: #eee; background: #eee;
...@@ -54,8 +53,6 @@ ...@@ -54,8 +53,6 @@
} }
&.wiki { &.wiki {
font-size: 14px;
line-height: 1.6;
padding: 25px; padding: 25px;
.highlight { .highlight {
......
...@@ -59,6 +59,10 @@ ...@@ -59,6 +59,10 @@
pre { pre {
white-space: pre; white-space: pre;
word-wrap: normal; word-wrap: normal;
code {
font-family: $monospace_font;
}
} }
} }
} }
...@@ -113,6 +113,11 @@ ...@@ -113,6 +113,11 @@
padding: 10px 15px; padding: 10px 15px;
} }
.cross-project-ref {
float: left;
padding: 10px 15px;
}
.creator { .creator {
float: right; float: right;
padding: 10px 15px; padding: 10px 15px;
......
/** Common mobile (screen XS) styles **/
@media (max-width: $screen-xs-max) {
.container .content {
margin-top: 20px;
}
.nav.nav-tabs > li > a {
padding: 10px;
font-size: 12px;
margin-right: 3px;
.badge {
display: none;
}
}
}
...@@ -75,3 +75,20 @@ ...@@ -75,3 +75,20 @@
} }
} }
} }
@media (max-width: $screen-xs-max) {
.timeline {
&:before {
background: none;
}
.timeline-entry .timeline-entry-inner {
.timeline-icon {
display: none;
}
.timeline-content {
margin-left: 0;
}
}
}
}
...@@ -29,28 +29,30 @@ ...@@ -29,28 +29,30 @@
.hljs-tag, .hljs-tag,
.hljs-tag .hljs-title, .hljs-tag .hljs-title,
.hljs-keyword,
.hljs-literal,
.hljs-strong, .hljs-strong,
.hljs-change, .hljs-change,
.hljs-winutils, .hljs-winutils,
.hljs-flow, .hljs-flow,
.lisp .hljs-title, .lisp .hljs-title,
.clojure .hljs-built_in, .clojure .hljs-built_in,
.hljs-keyword,
.nginx .hljs-title, .nginx .hljs-title,
.tex .hljs-special { .tex .hljs-special {
color: #F92672; color: #F92672;
} }
.hljs { .hljs {
color: #DDD; color: #F8F8F2;
} }
.hljs .hljs-constant, .asciidoc .hljs-code,
.asciidoc .hljs-code { .markdown .hljs-code,
.hljs-literal,
.hljs-function .hljs-keyword {
color: #66D9EF; color: #66D9EF;
} }
.hljs-code, .hljs-code,
.hljs-class .hljs-title, .hljs-class .hljs-title,
.hljs-header { .hljs-header {
...@@ -62,18 +64,27 @@ ...@@ -62,18 +64,27 @@
.hljs-symbol, .hljs-symbol,
.hljs-symbol .hljs-string, .hljs-symbol .hljs-string,
.hljs-value, .hljs-value,
.hljs-constant,
.hljs-number,
.hljs-regexp { .hljs-regexp {
color: #BF79DB; color: #AE81FF;
}
.hljs-string {
color: #E6DB74;
}
.hljs-params {
color: #fd971f;
} }
.hljs-link_url, .hljs-link_url,
.hljs-tag .hljs-value, .hljs-tag .hljs-value,
.hljs-string,
.hljs-bullet, .hljs-bullet,
.hljs-subst, .hljs-subst,
.hljs-title, .hljs-title,
.hljs-emphasis, .hljs-emphasis,
.haskell .hljs-type, .hljs-type,
.hljs-preprocessor, .hljs-preprocessor,
.hljs-pragma, .hljs-pragma,
.ruby .hljs-class .hljs-parent, .ruby .hljs-class .hljs-parent,
...@@ -99,12 +110,12 @@ ...@@ -99,12 +110,12 @@
} }
.hljs-comment, .hljs-comment,
.java .hljs-annotation, .hljs-annotation,
.smartquote, .smartquote,
.hljs-blockquote, .hljs-blockquote,
.hljs-horizontal_rule, .hljs-horizontal_rule,
.python .hljs-decorator,
.hljs-template_comment, .hljs-template_comment,
.hljs-decorator,
.hljs-pi, .hljs-pi,
.hljs-doctype, .hljs-doctype,
.hljs-deletion, .hljs-deletion,
......
...@@ -186,3 +186,11 @@ ...@@ -186,3 +186,11 @@
} }
} }
} }
.readme-holder .wiki, .note-body, .wiki-holder {
.white {
.highlight, pre, .hljs {
background: #F9F9F9;
}
}
}
/** Typo **/ /** Typo **/
$monospace_font: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono', 'lucida console', monospace; $monospace_font: 'Menlo', 'Liberation Mono', 'Consolas', 'DejaVu Sans Mono', 'Ubuntu Mono', 'Courier New', 'andale mono', 'lucida console', monospace;
$regular_font: "Helvetica Neue", Helvetica, Arial, sans-serif; $regular_font: "Helvetica Neue", Helvetica, Arial, sans-serif;
...@@ -58,8 +58,8 @@ ...@@ -58,8 +58,8 @@
} }
@mixin md-typography { @mixin md-typography {
font-size: 14px; font-size: 15px;
line-height: 1.6; line-height: 1.5;
img { img {
max-width: 100%; max-width: 100%;
...@@ -93,7 +93,7 @@ ...@@ -93,7 +93,7 @@
blockquote p { blockquote p {
color: #888; color: #888;
font-size: 14px; font-size: 15px;
line-height: 1.5; line-height: 1.5;
} }
......
...@@ -47,7 +47,7 @@ ...@@ -47,7 +47,7 @@
.event-title { .event-title {
@include str-truncated(72%); @include str-truncated(72%);
color: #333; color: #333;
font-weight: normal; font-weight: 500;
font-size: 14px; font-size: 14px;
.author_name { .author_name {
color: #333; color: #333;
...@@ -56,12 +56,9 @@ ...@@ -56,12 +56,9 @@
.event-body { .event-body {
margin-left: 35px; margin-left: 35px;
margin-right: 100px; margin-right: 100px;
color: #777;
.event-info {
color: #666;
}
.event-note { .event-note {
color: #666;
margin-top: 5px; margin-top: 5px;
.md { .md {
...@@ -72,7 +69,7 @@ ...@@ -72,7 +69,7 @@
border: none; border: none;
background: #f9f9f9; background: #f9f9f9;
border-radius: 0; border-radius: 0;
color: #666; color: #777;
margin: 0 20px; margin: 0 20px;
} }
...@@ -120,7 +117,6 @@ ...@@ -120,7 +117,6 @@
padding: 3px; padding: 3px;
padding-left: 0; padding-left: 0;
border: none; border: none;
color: #666;
.commit-row-title { .commit-row-title {
font-size: 12px; font-size: 12px;
} }
...@@ -186,7 +182,24 @@ ...@@ -186,7 +182,24 @@
} }
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
.event-item .event-title { .event-item {
@include str-truncated(65%); .event-title {
white-space: normal;
overflow: visible;
max-width: 100%;
}
.avatar {
display: none;
}
.event-body {
margin: 0;
border-left: 2px solid #DDD;
padding-left: 10px;
}
.event-item-timestamp {
display: none;
}
} }
} }
...@@ -59,6 +59,7 @@ header { ...@@ -59,6 +59,7 @@ header {
} }
.navbar-collapse { .navbar-collapse {
margin-top: 47px;
padding-right: 0; padding-right: 0;
padding-left: 0; padding-left: 0;
} }
......
...@@ -75,7 +75,7 @@ ...@@ -75,7 +75,7 @@
} }
.participants { .participants {
margin-bottom: 10px; margin-bottom: 20px;
} }
.issues_bulk_update { .issues_bulk_update {
...@@ -151,4 +151,14 @@ form.edit-issue { ...@@ -151,4 +151,14 @@ form.edit-issue {
} }
} }
} }
.issue {
&:hover .issue-actions {
display: none !important;
}
.issue-updated-at {
display: none;
}
}
} }
...@@ -113,30 +113,36 @@ ...@@ -113,30 +113,36 @@
font-size: 15px; font-size: 15px;
border-bottom: 1px solid #BBB; border-bottom: 1px solid #BBB;
color: #777; color: #777;
background-color: #F5F5F5;
&.ci-success { &.ci-success {
color: $bg_success; color: $bg_success;
border-color: $border_success; border-color: $border_success;
background-color: #F1FAF1;
} }
&.ci-pending { &.ci-pending {
color: #548; color: #548;
border-color: #548; border-color: #548;
background-color: #F4F1FA;
} }
&.ci-running { &.ci-running {
color: $bg_warning; color: $bg_warning;
border-color: $border_warning; border-color: $border_warning;
background-color: #FAF5F1;
} }
&.ci-failed { &.ci-failed {
color: $bg_danger; color: $bg_danger;
border-color: $border_danger; border-color: $border_danger;
background-color: #FAF1F1;
} }
&.ci-error { &.ci-error {
color: $bg_danger; color: $bg_danger;
border-color: $border_danger; border-color: $border_danger;
background-color: #FAF1F1;
} }
} }
......
...@@ -63,7 +63,6 @@ ...@@ -63,7 +63,6 @@
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
font-size: 18px; font-size: 18px;
margin: 0; margin: 0;
max-height: none; max-height: none;
&, .container { &, .container {
...@@ -86,6 +85,7 @@ ...@@ -86,6 +85,7 @@
color: #fff; color: #fff;
font-weight: normal; font-weight: normal;
text-shadow: none; text-shadow: none;
border: none;
&:after { display: none; } &:after { display: none; }
} }
......
...@@ -36,12 +36,15 @@ ul.notes { ...@@ -36,12 +36,15 @@ ul.notes {
font-size: 13px; font-size: 13px;
} }
.author { .author {
color: #555; color: #333;
font-weight: bold; font-weight: bold;
font-size: 14px; font-size: 14px;
&:hover { &:hover {
color: $link_hover_color; color: $link_color;
}
} }
.author-username {
font-size: 14px;
} }
} }
......
...@@ -111,3 +111,20 @@ ...@@ -111,3 +111,20 @@
height: 50px; height: 50px;
} }
} }
//CSS for password-strength indicator
#password-strength {
margin-bottom: 0;
}
.has-success input {
background-color: #D6F1D7 !important;
}
.has-error input {
background-color: #F3CECE !important;
}
.has-warning input {
background-color: #FFE9A4 !important;
}
...@@ -270,3 +270,41 @@ ul.nav.nav-projects-tabs { ...@@ -270,3 +270,41 @@ ul.nav.nav-projects-tabs {
color: #999; color: #999;
} }
} }
.fork-namespaces {
.thumbnail {
&.fork-exists-thumbnail {
border-color: #EEE;
.caption {
color: #999;
}
}
&.fork-thumbnail {
border-color: #AAA;
&:hover {
background-color: $hover;
}
}
a {
text-decoration: none;
}
}
}
@media (max-width: $screen-xs-max) {
.project-home-panel {
.star-fork-buttons {
padding-top: 10px;
padding-right: 15px;
}
}
.project-home-links {
display: none;
}
}
class Admin::BackgroundJobsController < Admin::ApplicationController class Admin::BackgroundJobsController < Admin::ApplicationController
def show def show
ps_output, _ = Gitlab::Popen.popen(%W(ps -U #{Settings.gitlab.user} -o pid,pcpu,pmem,stat,start,command)) ps_output, _ = Gitlab::Popen.popen(%W(ps -U #{Gitlab.config.gitlab.user} -o pid,pcpu,pmem,stat,start,command))
@sidekiq_processes = ps_output.split("\n").grep(/sidekiq/) @sidekiq_processes = ps_output.split("\n").grep(/sidekiq/)
end end
end end
...@@ -31,17 +31,11 @@ class Admin::ProjectsController < Admin::ApplicationController ...@@ -31,17 +31,11 @@ class Admin::ProjectsController < Admin::ApplicationController
protected protected
def project def project
id = params[:project_id] || params[:id] @project = Project.find_with_namespace(params[:id])
@project = Project.find_with_namespace(id)
@project || render_404 @project || render_404
end end
def group def group
@group ||= project.group @group ||= @project.group
end
def repository
@repository ||= project.repository
end end
end end
...@@ -5,9 +5,7 @@ class ApplicationController < ActionController::Base ...@@ -5,9 +5,7 @@ class ApplicationController < ActionController::Base
before_filter :authenticate_user! before_filter :authenticate_user!
before_filter :reject_blocked! before_filter :reject_blocked!
before_filter :check_password_expiration before_filter :check_password_expiration
before_filter :add_abilities
before_filter :ldap_security_check before_filter :ldap_security_check
before_filter :dev_tools if Rails.env == 'development'
before_filter :default_headers before_filter :default_headers
before_filter :add_gon_variables before_filter :add_gon_variables
before_filter :configure_permitted_parameters, if: :devise_controller? before_filter :configure_permitted_parameters, if: :devise_controller?
...@@ -73,7 +71,7 @@ class ApplicationController < ActionController::Base ...@@ -73,7 +71,7 @@ class ApplicationController < ActionController::Base
end end
def abilities def abilities
@abilities ||= Six.new Ability.abilities
end end
def can?(object, action, subject) def can?(object, action, subject)
...@@ -81,6 +79,7 @@ class ApplicationController < ActionController::Base ...@@ -81,6 +79,7 @@ class ApplicationController < ActionController::Base
end end
def project def project
unless @project
id = params[:project_id] || params[:id] id = params[:project_id] || params[:id]
# Redirect from # Redirect from
...@@ -104,6 +103,8 @@ class ApplicationController < ActionController::Base ...@@ -104,6 +103,8 @@ class ApplicationController < ActionController::Base
render_404 and return render_404 and return
end end
end end
@project
end
def repository def repository
@repository ||= project.repository @repository ||= project.repository
...@@ -111,22 +112,10 @@ class ApplicationController < ActionController::Base ...@@ -111,22 +112,10 @@ class ApplicationController < ActionController::Base
nil nil
end end
def add_abilities
abilities << Ability
end
def authorize_project!(action) def authorize_project!(action)
return access_denied! unless can?(current_user, action, project) return access_denied! unless can?(current_user, action, project)
end end
def authorize_code_access!
return access_denied! unless can?(current_user, :download_code, project)
end
def authorize_push!
return access_denied! unless can?(current_user, :push_code, project)
end
def authorize_labels! def authorize_labels!
# Labels should be accessible for issues and/or merge requests # Labels should be accessible for issues and/or merge requests
authorize_read_issue! || authorize_read_merge_request! authorize_read_issue! || authorize_read_merge_request!
...@@ -170,9 +159,6 @@ class ApplicationController < ActionController::Base ...@@ -170,9 +159,6 @@ class ApplicationController < ActionController::Base
response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT" response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT"
end end
def dev_tools
end
def default_headers def default_headers
headers['X-Frame-Options'] = 'DENY' headers['X-Frame-Options'] = 'DENY'
headers['X-XSS-Protection'] = '1; mode=block' headers['X-XSS-Protection'] = '1; mode=block'
......
class Explore::GroupsController < ApplicationController class Explore::GroupsController < ApplicationController
skip_before_filter :authenticate_user!, skip_before_filter :authenticate_user!,
:reject_blocked, :set_current_user_for_observers, :reject_blocked, :set_current_user_for_observers
:add_abilities
layout "explore" layout "explore"
......
class Explore::ProjectsController < ApplicationController class Explore::ProjectsController < ApplicationController
skip_before_filter :authenticate_user!, skip_before_filter :authenticate_user!,
:reject_blocked, :reject_blocked
:add_abilities
layout 'explore' layout 'explore'
......
...@@ -19,6 +19,7 @@ class Groups::GroupMembersController < ApplicationController ...@@ -19,6 +19,7 @@ class Groups::GroupMembersController < ApplicationController
def destroy def destroy
@users_group = @group.group_members.find(params[:id]) @users_group = @group.group_members.find(params[:id])
if can?(current_user, :destroy, @users_group) # May fail if last owner. if can?(current_user, :destroy, @users_group) # May fail if last owner.
@users_group.destroy @users_group.destroy
respond_to do |format| respond_to do |format|
......
...@@ -42,25 +42,32 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController ...@@ -42,25 +42,32 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
def handle_omniauth def handle_omniauth
if current_user if current_user
# Change a logged-in user's authentication method: # Add new authentication method
current_user.extern_uid = oauth['uid'] current_user.identities.find_or_create_by(extern_uid: oauth['uid'], provider: oauth['provider'])
current_user.provider = oauth['provider']
current_user.save
redirect_to profile_path redirect_to profile_path
else else
@user = Gitlab::OAuth::User.new(oauth) @user = Gitlab::OAuth::User.new(oauth)
if Gitlab.config.omniauth['allow_single_sign_on'] && @user.new?
@user.save @user.save
end
if @user.valid? # Only allow properly saved users to login.
if @user.persisted? && @user.valid?
sign_in_and_redirect(@user.gl_user) sign_in_and_redirect(@user.gl_user)
else else
error_message = @user.gl_user.errors.map{ |attribute, message| "#{attribute} #{message}" }.join(", ") error_message =
if @user.gl_user.errors.any?
@user.gl_user.errors.map do |attribute, message|
"#{attribute} #{message}"
end.join(", ")
else
''
end
redirect_to omniauth_error_path(oauth['provider'], error: error_message) and return redirect_to omniauth_error_path(oauth['provider'], error: error_message) and return
end end
end end
rescue ForbiddenAction => e
flash[:notice] = e.message
redirect_to new_user_session_path
end end
def oauth def oauth
......
...@@ -29,4 +29,31 @@ class Projects::ApplicationController < ApplicationController ...@@ -29,4 +29,31 @@ class Projects::ApplicationController < ApplicationController
redirect_to project_tree_path(@project, @ref), notice: "This action is not allowed unless you are on top of a branch" redirect_to project_tree_path(@project, @ref), notice: "This action is not allowed unless you are on top of a branch"
end end
end end
def set_filter_variables(collection)
params[:sort] ||= 'newest'
params[:scope] = 'all' if params[:scope].blank?
params[:state] = 'opened' if params[:state].blank?
@sort = params[:sort].humanize
assignee_id = params[:assignee_id]
author_id = params[:author_id]
milestone_id = params[:milestone_id]
if assignee_id.present? && !assignee_id.to_i.zero?
@assignee = @project.team.find(assignee_id)
end
if author_id.present? && !author_id.to_i.zero?
@author = @project.team.find(assignee_id)
end
if milestone_id.present? && !milestone_id.to_i.zero?
@milestone = @project.milestones.find(milestone_id)
end
@assignees = User.where(id: collection.pluck(:assignee_id))
@authors = User.where(id: collection.pluck(:author_id))
end
end end
class Projects::BaseTreeController < Projects::ApplicationController class Projects::BaseTreeController < Projects::ApplicationController
include ExtractsPath include ExtractsPath
before_filter :authorize_read_project! before_filter :authorize_download_code!
before_filter :authorize_code_access!
before_filter :require_non_empty_project before_filter :require_non_empty_project
end end
...@@ -3,8 +3,7 @@ class Projects::BlameController < Projects::ApplicationController ...@@ -3,8 +3,7 @@ class Projects::BlameController < Projects::ApplicationController
include ExtractsPath include ExtractsPath
# Authorize # Authorize
before_filter :authorize_read_project! before_filter :authorize_download_code!
before_filter :authorize_code_access!
before_filter :require_non_empty_project before_filter :require_non_empty_project
def show def show
......
...@@ -3,10 +3,9 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -3,10 +3,9 @@ class Projects::BlobController < Projects::ApplicationController
include ExtractsPath include ExtractsPath
# Authorize # Authorize
before_filter :authorize_read_project! before_filter :authorize_download_code!
before_filter :authorize_code_access!
before_filter :require_non_empty_project before_filter :require_non_empty_project
before_filter :authorize_push!, only: [:destroy] before_filter :authorize_push_code!, only: [:destroy]
before_filter :blob before_filter :blob
...@@ -20,7 +19,7 @@ class Projects::BlobController < Projects::ApplicationController ...@@ -20,7 +19,7 @@ class Projects::BlobController < Projects::ApplicationController
flash[:notice] = "Your changes have been successfully committed" flash[:notice] = "Your changes have been successfully committed"
redirect_to project_tree_path(@project, @ref) redirect_to project_tree_path(@project, @ref)
else else
flash[:alert] = result[:error] flash[:alert] = result[:message]
render :show render :show
end end
end end
......
class Projects::BranchesController < Projects::ApplicationController class Projects::BranchesController < Projects::ApplicationController
include ActionView::Helpers::SanitizeHelper
# Authorize # Authorize
before_filter :authorize_read_project!
before_filter :require_non_empty_project before_filter :require_non_empty_project
before_filter :authorize_code_access! before_filter :authorize_download_code!
before_filter :authorize_push!, only: [:create, :destroy] before_filter :authorize_push_code!, only: [:create, :destroy]
def index def index
@sort = params[:sort] || 'name' @sort = params[:sort] || 'name'
...@@ -17,8 +17,11 @@ class Projects::BranchesController < Projects::ApplicationController ...@@ -17,8 +17,11 @@ class Projects::BranchesController < Projects::ApplicationController
end end
def create def create
branch_name = sanitize(strip_tags(params[:branch_name]))
ref = sanitize(strip_tags(params[:ref]))
result = CreateBranchService.new(project, current_user). result = CreateBranchService.new(project, current_user).
execute(params[:branch_name], params[:ref]) execute(branch_name, ref)
if result[:status] == :success if result[:status] == :success
@branch = result[:branch] @branch = result[:branch]
redirect_to project_tree_path(@project, @branch.name) redirect_to project_tree_path(@project, @branch.name)
......
...@@ -3,20 +3,19 @@ ...@@ -3,20 +3,19 @@
# Not to be confused with CommitsController, plural. # Not to be confused with CommitsController, plural.
class Projects::CommitController < Projects::ApplicationController class Projects::CommitController < Projects::ApplicationController
# Authorize # Authorize
before_filter :authorize_read_project! before_filter :authorize_download_code!
before_filter :authorize_code_access!
before_filter :require_non_empty_project before_filter :require_non_empty_project
before_filter :commit before_filter :commit
def show def show
return git_not_found! unless @commit return git_not_found! unless @commit
@line_notes = project.notes.for_commit_id(commit.id).inline @line_notes = @project.notes.for_commit_id(commit.id).inline
@branches = project.repository.branch_names_contains(commit.id) @branches = @project.repository.branch_names_contains(commit.id)
@diffs = @commit.diffs @diffs = @commit.diffs
@note = project.build_commit_note(commit) @note = @project.build_commit_note(commit)
@notes_count = project.notes.for_commit_id(commit.id).count @notes_count = @project.notes.for_commit_id(commit.id).count
@notes = project.notes.for_commit_id(@commit.id).not_inline.fresh @notes = @project.notes.for_commit_id(@commit.id).not_inline.fresh
@noteable = @commit @noteable = @commit
@comments_allowed = @reply_allowed = true @comments_allowed = @reply_allowed = true
@comments_target = { @comments_target = {
...@@ -32,6 +31,6 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -32,6 +31,6 @@ class Projects::CommitController < Projects::ApplicationController
end end
def commit def commit
@commit ||= project.repository.commit(params[:id]) @commit ||= @project.repository.commit(params[:id])
end end
end end
...@@ -4,8 +4,7 @@ class Projects::CommitsController < Projects::ApplicationController ...@@ -4,8 +4,7 @@ class Projects::CommitsController < Projects::ApplicationController
include ExtractsPath include ExtractsPath
# Authorize # Authorize
before_filter :authorize_read_project! before_filter :authorize_download_code!
before_filter :authorize_code_access!
before_filter :require_non_empty_project before_filter :require_non_empty_project
def show def show
......
class Projects::CompareController < Projects::ApplicationController class Projects::CompareController < Projects::ApplicationController
# Authorize # Authorize
before_filter :authorize_read_project! before_filter :authorize_download_code!
before_filter :authorize_code_access!
before_filter :require_non_empty_project before_filter :require_non_empty_project
def index def index
......
...@@ -42,7 +42,7 @@ class Projects::DeployKeysController < Projects::ApplicationController ...@@ -42,7 +42,7 @@ class Projects::DeployKeysController < Projects::ApplicationController
end end
def enable def enable
project.deploy_keys << available_keys.find(params[:id]) @project.deploy_keys << available_keys.find(params[:id])
redirect_to project_deploy_keys_path(@project) redirect_to project_deploy_keys_path(@project)
end end
......
class Projects::EditTreeController < Projects::BaseTreeController class Projects::EditTreeController < Projects::BaseTreeController
before_filter :require_branch_head before_filter :require_branch_head
before_filter :blob before_filter :blob
before_filter :authorize_push! before_filter :authorize_push_code!
before_filter :from_merge_request before_filter :from_merge_request
before_filter :after_edit_path before_filter :after_edit_path
...@@ -22,7 +22,7 @@ class Projects::EditTreeController < Projects::BaseTreeController ...@@ -22,7 +22,7 @@ class Projects::EditTreeController < Projects::BaseTreeController
redirect_to after_edit_path redirect_to after_edit_path
else else
flash[:alert] = result[:error] flash[:alert] = result[:message]
render :show render :show
end end
end end
......
class Projects::ForksController < Projects::ApplicationController
# Authorize
before_filter :authorize_download_code!
before_filter :require_non_empty_project
def new
@namespaces = current_user.manageable_namespaces
@namespaces.delete(@project.namespace)
end
def create
namespace = Namespace.find(params[:namespace_id])
@forked_project = ::Projects::ForkService.new(project, current_user, namespace: namespace).execute
if @forked_project.saved? && @forked_project.forked?
redirect_to(@forked_project, notice: 'Project was successfully forked.')
else
@title = 'Fork project'
render :error
end
end
end
class Projects::GraphsController < Projects::ApplicationController class Projects::GraphsController < Projects::ApplicationController
# Authorize # Authorize
before_filter :authorize_read_project! before_filter :authorize_download_code!
before_filter :authorize_code_access!
before_filter :require_non_empty_project before_filter :require_non_empty_project
def show def show
......
...@@ -26,6 +26,7 @@ class Projects::HooksController < Projects::ApplicationController ...@@ -26,6 +26,7 @@ class Projects::HooksController < Projects::ApplicationController
def test def test
if !@project.empty_repo? if !@project.empty_repo?
status = TestHookService.new.execute(hook, current_user) status = TestHookService.new.execute(hook, current_user)
if status if status
flash[:notice] = 'Hook successfully executed.' flash[:notice] = 'Hook successfully executed.'
else else
......
class Projects::ImportsController < Projects::ApplicationController
# Authorize
before_filter :authorize_admin_project!
before_filter :require_no_repo
before_filter :redirect_if_progress, except: :show
def new
end
def create
@project.import_url = params[:project][:import_url]
if @project.save
@project.reload
if @project.import_failed?
@project.import_retry
else
@project.import_start
end
end
redirect_to project_import_path(@project)
end
def show
unless @project.import_in_progress?
if @project.import_finished?
redirect_to(@project) and return
else
redirect_to new_project_import_path(@project) and return
end
end
end
private
def require_no_repo
if @project.repository_exists?
redirect_to(@project) and return
end
end
def redirect_if_progress
if @project.import_in_progress?
redirect_to project_import_path(@project) and return
end
end
end
...@@ -18,18 +18,12 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -18,18 +18,12 @@ class Projects::IssuesController < Projects::ApplicationController
def index def index
terms = params['issue_search'] terms = params['issue_search']
set_filter_variables(@project.issues)
@issues = issues_filtered @issues = IssuesFinder.new.execute(current_user, params.merge(project_id: @project.id))
@issues = @issues.full_search(terms) if terms.present? @issues = @issues.full_search(terms) if terms.present?
@issues = @issues.page(params[:page]).per(20) @issues = @issues.page(params[:page]).per(20)
assignee_id, milestone_id = params[:assignee_id], params[:milestone_id]
@assignee = @project.team.find(assignee_id) if assignee_id.present? && !assignee_id.to_i.zero?
@milestone = @project.milestones.find(milestone_id) if milestone_id.present? && !milestone_id.to_i.zero?
sort_param = params[:sort] || 'newest'
@sort = sort_param.humanize unless sort_param.empty?
@assignees = User.where(id: @project.issues.pluck(:assignee_id)).active
respond_to do |format| respond_to do |format|
format.html format.html
format.atom { render layout: false } format.atom { render layout: false }
...@@ -127,12 +121,6 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -127,12 +121,6 @@ class Projects::IssuesController < Projects::ApplicationController
return render_404 unless @project.issues_enabled return render_404 unless @project.issues_enabled
end end
def issues_filtered
params[:scope] = 'all' if params[:scope].blank?
params[:state] = 'opened' if params[:state].blank?
@issues = IssuesFinder.new.execute(current_user, params.merge(project_id: @project.id))
end
# Since iids are implemented only in 6.1 # Since iids are implemented only in 6.1
# user may navigate to issue page using old global ids. # user may navigate to issue page using old global ids.
# #
......
...@@ -17,18 +17,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -17,18 +17,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController
before_filter :authorize_modify_merge_request!, only: [:close, :edit, :update, :sort] before_filter :authorize_modify_merge_request!, only: [:close, :edit, :update, :sort]
def index def index
params[:sort] ||= 'newest' set_filter_variables(@project.merge_requests)
params[:scope] = 'all' if params[:scope].blank?
params[:state] = 'opened' if params[:state].blank?
@merge_requests = MergeRequestsFinder.new.execute(current_user, params.merge(project_id: @project.id)) @merge_requests = MergeRequestsFinder.new.execute(current_user, params.merge(project_id: @project.id))
@merge_requests = @merge_requests.page(params[:page]).per(20) @merge_requests = @merge_requests.page(params[:page]).per(20)
@sort = params[:sort].humanize
assignee_id, milestone_id = params[:assignee_id], params[:milestone_id]
@assignee = @project.team.find(assignee_id) if assignee_id.present? && !assignee_id.to_i.zero?
@milestone = @project.milestones.find(milestone_id) if milestone_id.present? && !milestone_id.to_i.zero?
@assignees = User.where(id: @project.merge_requests.pluck(:assignee_id))
end end
def show def show
...@@ -225,6 +217,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -225,6 +217,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@allowed_to_merge = allowed_to_merge? @allowed_to_merge = allowed_to_merge?
@show_merge_controls = @merge_request.open? && @commits.any? && @allowed_to_merge @show_merge_controls = @merge_request.open? && @commits.any? && @allowed_to_merge
@source_branch = @merge_request.source_project.repository.find_branch(@merge_request.source_branch).try(:name) @source_branch = @merge_request.source_project.repository.find_branch(@merge_request.source_branch).try(:name)
if @merge_request.locked_long_ago?
@merge_request.unlock_mr
@merge_request.close
end
end end
def allowed_to_merge? def allowed_to_merge?
......
...@@ -103,7 +103,9 @@ class Projects::MilestonesController < Projects::ApplicationController ...@@ -103,7 +103,9 @@ class Projects::MilestonesController < Projects::ApplicationController
end end
def module_enabled def module_enabled
return render_404 unless @project.issues_enabled unless @project.issues_enabled || @project.merge_requests_enabled
return render_404
end
end end
def milestone_params def milestone_params
......
...@@ -3,8 +3,7 @@ class Projects::NetworkController < Projects::ApplicationController ...@@ -3,8 +3,7 @@ class Projects::NetworkController < Projects::ApplicationController
include ApplicationHelper include ApplicationHelper
# Authorize # Authorize
before_filter :authorize_read_project! before_filter :authorize_download_code!
before_filter :authorize_code_access!
before_filter :require_non_empty_project before_filter :require_non_empty_project
def show def show
......
class Projects::NewTreeController < Projects::BaseTreeController class Projects::NewTreeController < Projects::BaseTreeController
before_filter :require_branch_head before_filter :require_branch_head
before_filter :authorize_push! before_filter :authorize_push_code!
def show def show
end end
......
...@@ -3,8 +3,7 @@ class Projects::RawController < Projects::ApplicationController ...@@ -3,8 +3,7 @@ class Projects::RawController < Projects::ApplicationController
include ExtractsPath include ExtractsPath
# Authorize # Authorize
before_filter :authorize_read_project! before_filter :authorize_download_code!
before_filter :authorize_code_access!
before_filter :require_non_empty_project before_filter :require_non_empty_project
def show def show
......
...@@ -2,8 +2,7 @@ class Projects::RefsController < Projects::ApplicationController ...@@ -2,8 +2,7 @@ class Projects::RefsController < Projects::ApplicationController
include ExtractsPath include ExtractsPath
# Authorize # Authorize
before_filter :authorize_read_project! before_filter :authorize_download_code!
before_filter :authorize_code_access!
before_filter :require_non_empty_project before_filter :require_non_empty_project
def switch def switch
......
class Projects::RepositoriesController < Projects::ApplicationController class Projects::RepositoriesController < Projects::ApplicationController
# Authorize # Authorize
before_filter :authorize_read_project! before_filter :authorize_download_code!
before_filter :authorize_code_access! before_filter :require_non_empty_project, except: :create
before_filter :require_non_empty_project before_filter :authorize_admin_project!, only: :create
def create
@project.create_repository
redirect_to @project
end
def archive def archive
unless can?(current_user, :download_code, @project) unless can?(current_user, :download_code, @project)
......
...@@ -41,7 +41,8 @@ class Projects::ServicesController < Projects::ApplicationController ...@@ -41,7 +41,8 @@ class Projects::ServicesController < Projects::ApplicationController
params.require(:service).permit( params.require(:service).permit(
:title, :token, :type, :active, :api_key, :subdomain, :title, :token, :type, :active, :api_key, :subdomain,
:room, :recipients, :project_url, :webhook, :room, :recipients, :project_url, :webhook,
:user_key, :device, :priority, :sound :user_key, :device, :priority, :sound, :bamboo_url, :username, :password,
:build_key, :server
) )
end end
end end
class Projects::TagsController < Projects::ApplicationController class Projects::TagsController < Projects::ApplicationController
# Authorize # Authorize
before_filter :authorize_read_project!
before_filter :require_non_empty_project before_filter :require_non_empty_project
before_filter :authorize_download_code!
before_filter :authorize_code_access! before_filter :authorize_push_code!, only: [:create]
before_filter :authorize_push!, only: [:create]
before_filter :authorize_admin_project!, only: [:destroy] before_filter :authorize_admin_project!, only: [:destroy]
def index def index
......
...@@ -10,7 +10,7 @@ class Projects::TeamMembersController < Projects::ApplicationController ...@@ -10,7 +10,7 @@ class Projects::TeamMembersController < Projects::ApplicationController
end end
def new def new
@user_project_relation = project.project_members.new @user_project_relation = @project.project_members.new
end end
def create def create
...@@ -26,7 +26,7 @@ class Projects::TeamMembersController < Projects::ApplicationController ...@@ -26,7 +26,7 @@ class Projects::TeamMembersController < Projects::ApplicationController
end end
def update def update
@user_project_relation = project.project_members.find_by(user_id: member) @user_project_relation = @project.project_members.find_by(user_id: member)
@user_project_relation.update_attributes(member_params) @user_project_relation.update_attributes(member_params)
unless @user_project_relation.valid? unless @user_project_relation.valid?
...@@ -36,7 +36,7 @@ class Projects::TeamMembersController < Projects::ApplicationController ...@@ -36,7 +36,7 @@ class Projects::TeamMembersController < Projects::ApplicationController
end end
def destroy def destroy
@user_project_relation = project.project_members.find_by(user_id: member) @user_project_relation = @project.project_members.find_by(user_id: member)
@user_project_relation.destroy @user_project_relation.destroy
respond_to do |format| respond_to do |format|
...@@ -46,7 +46,7 @@ class Projects::TeamMembersController < Projects::ApplicationController ...@@ -46,7 +46,7 @@ class Projects::TeamMembersController < Projects::ApplicationController
end end
def leave def leave
project.project_members.find_by(user_id: current_user).destroy @project.project_members.find_by(user_id: current_user).destroy
respond_to do |format| respond_to do |format|
format.html { redirect_to :back } format.html { redirect_to :back }
......
...@@ -4,9 +4,7 @@ class ProjectsController < ApplicationController ...@@ -4,9 +4,7 @@ class ProjectsController < ApplicationController
before_filter :repository, except: [:new, :create] before_filter :repository, except: [:new, :create]
# Authorize # Authorize
before_filter :authorize_read_project!, except: [:index, :new, :create] before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive]
before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer, :archive, :unarchive, :retry_import]
before_filter :require_non_empty_project, only: [:blob, :tree, :graph]
layout 'navless', only: [:new, :create, :fork] layout 'navless', only: [:new, :create, :fork]
before_filter :set_title, only: [:new, :create] before_filter :set_title, only: [:new, :create]
...@@ -21,10 +19,11 @@ class ProjectsController < ApplicationController ...@@ -21,10 +19,11 @@ class ProjectsController < ApplicationController
def create def create
@project = ::Projects::CreateService.new(current_user, project_params).execute @project = ::Projects::CreateService.new(current_user, project_params).execute
flash[:notice] = 'Project was successfully created.' if @project.saved?
respond_to do |format| if @project.saved?
format.js redirect_to project_path(@project), notice: 'Project was successfully created.'
else
render 'new'
end end
end end
...@@ -49,12 +48,10 @@ class ProjectsController < ApplicationController ...@@ -49,12 +48,10 @@ class ProjectsController < ApplicationController
def show def show
if @project.import_in_progress? if @project.import_in_progress?
redirect_to import_project_path(@project) redirect_to project_import_path(@project)
return return
end end
return authenticate_user! unless @project.public? || current_user
limit = (params[:limit] || 20).to_i limit = (params[:limit] || 20).to_i
@events = @project.events.recent @events = @project.events.recent
@events = event_filter.apply_filter(@events) @events = event_filter.apply_filter(@events)
...@@ -64,41 +61,24 @@ class ProjectsController < ApplicationController ...@@ -64,41 +61,24 @@ class ProjectsController < ApplicationController
respond_to do |format| respond_to do |format|
format.html do format.html do
if @project.repository_exists?
if @project.empty_repo? if @project.empty_repo?
render "projects/empty", layout: user_layout render "projects/empty", layout: user_layout
else else
@last_push = current_user.recent_push(@project.id) if current_user @last_push = current_user.recent_push(@project.id) if current_user
render :show, layout: user_layout render :show, layout: user_layout
end end
else
render "projects/no_repo", layout: user_layout
end end
format.json { pager_json("events/_events", @events.count) }
end
end
def import
if project.import_finished?
redirect_to @project
return
end
end
def retry_import
unless @project.import_failed?
redirect_to import_project_path(@project)
end end
@project.import_url = project_params[:import_url] format.json { pager_json("events/_events", @events.count) }
if @project.save
@project.reload
@project.import_retry
end end
redirect_to import_project_path(@project)
end end
def destroy def destroy
return access_denied! unless can?(current_user, :remove_project, project) return access_denied! unless can?(current_user, :remove_project, @project)
::Projects::DestroyService.new(@project, current_user, {}).execute ::Projects::DestroyService.new(@project, current_user, {}).execute
...@@ -115,22 +95,6 @@ class ProjectsController < ApplicationController ...@@ -115,22 +95,6 @@ class ProjectsController < ApplicationController
end end
end end
def fork
@forked_project = ::Projects::ForkService.new(project, current_user).execute
respond_to do |format|
format.html do
if @forked_project.saved? && @forked_project.forked?
redirect_to(@forked_project, notice: 'Project was successfully forked.')
else
@title = 'Fork project'
render "fork"
end
end
format.js
end
end
def autocomplete_sources def autocomplete_sources
note_type = params['type'] note_type = params['type']
note_id = params['type_id'] note_id = params['type_id']
...@@ -148,8 +112,8 @@ class ProjectsController < ApplicationController ...@@ -148,8 +112,8 @@ class ProjectsController < ApplicationController
end end
def archive def archive
return access_denied! unless can?(current_user, :archive_project, project) return access_denied! unless can?(current_user, :archive_project, @project)
project.archive! @project.archive!
respond_to do |format| respond_to do |format|
format.html { redirect_to @project } format.html { redirect_to @project }
...@@ -157,8 +121,8 @@ class ProjectsController < ApplicationController ...@@ -157,8 +121,8 @@ class ProjectsController < ApplicationController
end end
def unarchive def unarchive
return access_denied! unless can?(current_user, :archive_project, project) return access_denied! unless can?(current_user, :archive_project, @project)
project.unarchive! @project.unarchive!
respond_to do |format| respond_to do |format|
format.html { redirect_to @project } format.html { redirect_to @project }
......
...@@ -9,7 +9,7 @@ class SnippetsController < ApplicationController ...@@ -9,7 +9,7 @@ class SnippetsController < ApplicationController
before_filter :set_title before_filter :set_title
skip_before_filter :authenticate_user!, only: [:index, :user_index] skip_before_filter :authenticate_user!, only: [:index, :user_index, :show, :raw]
respond_to :html respond_to :html
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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