Commit 1b54b212 authored by Sacred Seven's avatar Sacred Seven

Merged v7.7.1

parents 56565a0b 41ab9e1f
*.log
*.swp
.DS_Store
.bundle .bundle
.chef
.directory
.envrc
.gitlab_shell_secret
.idea
.rbenv-version
.rbx/ .rbx/
db/*.sqlite3
db/*.sqlite3-journal
log/*.log*
tmp/
.sass-cache/
coverage/*
backups/*
*.swp
public/uploads/
.ruby-version
.ruby-gemset .ruby-gemset
.ruby-version
.rvmrc .rvmrc
.rbenv-version .sass-cache/
.directory .secret
nohup.out
Vagrantfile
.vagrant .vagrant
config/gitlab.yml Vagrantfile
backups/*
config/aws.yml
config/database.yml config/database.yml
config/gitlab.yml
config/initializers/omniauth.rb config/initializers/omniauth.rb
config/initializers/rack_attack.rb config/initializers/rack_attack.rb
config/initializers/smtp_settings.rb config/initializers/smtp_settings.rb
config/unicorn.rb
config/resque.yml config/resque.yml
config/aws.yml config/unicorn.rb
coverage/*
db/*.sqlite3
db/*.sqlite3-journal
db/data.yml db/data.yml
.idea
.DS_Store
.chef
vendor/bundle/*
rails_best_practices_output.html
doc/code/* doc/code/*
.secret
*.log
public/uploads.*
public/assets/
.envrc
dump.rdb dump.rdb
log/*.log*
nohup.out
public/assets/
public/uploads.*
public/uploads/
rails_best_practices_output.html
tags tags
.AppleDouble .AppleDouble
.gitlab_shell_secret tmp/
vendor/bundle/*
Note: The upcoming release contains empty lines to reduce the number of merge conflicts, scroll down to see past releases.
v 7.7.0
- Import from GitHub.com feature
- Add Jetbrains Teamcity CI service (Jason Lippert)
- Mention notification level
- Markdown preview in wiki (Yuriy Glukhov)
- Raise group avatar filesize limit to 200kb
- OAuth applications feature
- Show user SSH keys in admin area
- Developer can push to protected branches option
- Set project path instead of project name in create form
- Block Git HTTP access after 10 failed authentication attempts
- Updates to the messages returned by API (sponsored by O'Reilly Media)
- New UI layout with side navigation
- Add alert message in case of outdated browser (IE < 10)
- Added API support for sorting projects
- Update gitlab_git to version 7.0.0.rc14
- Add API project search filter option for authorized projects
- Fix File blame not respecting branch selection
- Change some of application settings on fly in admin area UI
- Redesign signin/signup pages
- Close standard input in Gitlab::Popen.popen
- Trigger GitLab CI when push tags
- When accept merge request - do merge using sidaekiq job
- Enable web signups by default
- Fixes for diff comments: drag-n-drop images, selecting images
- Fixes for edit comments: drag-n-drop images, preview mode, selecting images, save & update
- Remove password strength indicator
- Fix commit pagination
v 7.6.0 v 7.6.0
- Fork repository to groups - Fork repository to groups
- New rugged version - New rugged version
...@@ -22,8 +55,15 @@ v 7.6.0 ...@@ -22,8 +55,15 @@ v 7.6.0
- Possibility to create Milestones or Labels when Issues are disabled - Possibility to create Milestones or Labels when Issues are disabled
- Fix bug with showing gpg signature in tag - Fix bug with showing gpg signature in tag
v 7.5.3
- Bump gitlab_git to 7.0.0.rc12 (includes Rugged 0.21.2)
v 7.5.2 v 7.5.2
- Don't log Sidekiq arguments by default - Don't log Sidekiq arguments by default
- Fix restore of wiki repositories from backups
v 7.5.1
- Add missing timestamps to 'members' table
v 7.5.0 v 7.5.0
- API: Add support for Hipchat (Kevin Houdebert) - API: Add support for Hipchat (Kevin Houdebert)
...@@ -43,7 +83,7 @@ v 7.5.0 ...@@ -43,7 +83,7 @@ v 7.5.0
- Performance improvements - Performance improvements
- Fix post-receive issue for projects with deleted forks - Fix post-receive issue for projects with deleted forks
- New gitlab-shell version with custom hooks support - New gitlab-shell version with custom hooks support
- Improve code - Improve code
- GitLab CI 5.2+ support (does not support older versions) - GitLab CI 5.2+ support (does not support older versions)
- Fixed bug when you can not push commits starting with 000000 to protected branches - Fixed bug when you can not push commits starting with 000000 to protected branches
- Added a password strength indicator - Added a password strength indicator
......
...@@ -101,6 +101,16 @@ Please ensure you support the feature you contribute through all of these steps. ...@@ -101,6 +101,16 @@ Please ensure you support the feature you contribute through all of these steps.
1. Community questions answered 1. Community questions answered
1. Answers to questions radiated (in docs/wiki/etc.) 1. Answers to questions radiated (in docs/wiki/etc.)
If you add a dependency in GitLab (such as an operating system package) please consider updating the following and note the applicability of each in your merge request:
1. Note the addition in the release blog post (create one if it doesn't exist yet) https://gitlab.com/gitlab-com/www-gitlab-com/merge_requests/
1. Upgrade guide, for example https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/update/7.5-to-7.6.md
1. Upgrader https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/update/upgrader.md#2-run-gitlab-upgrade-tool
1. Installation guide https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/installation.md#1-packages-dependencies
1. GitLab Development Kit https://gitlab.com/gitlab-org/gitlab-development-kit
1. Test suite https://gitlab.com/gitlab-org/gitlab-ci/blob/master/doc/examples/configure_a_runner_to_run_the_gitlab_ce_test_suite.md
1. Omnibus package creator https://gitlab.com/gitlab-org/omnibus-gitlab
## Merge request description format ## Merge request description format
1. What does this MR do? 1. What does this MR do?
...@@ -118,6 +128,7 @@ Please ensure you support the feature you contribute through all of these steps. ...@@ -118,6 +128,7 @@ Please ensure you support the feature you contribute through all of these steps.
1. Can merge without problems (if not please merge `master`, never rebase commits pushed to the remote server) 1. Can merge without problems (if not please merge `master`, never rebase commits pushed to the remote server)
1. Does not break any existing functionality 1. Does not break any existing functionality
1. Fixes one specific issue or implements one specific feature (do not combine things, send separate merge requests if needed) 1. Fixes one specific issue or implements one specific feature (do not combine things, send separate merge requests if needed)
1. Migrations should do only one thing (eg: either create a table, move data to a new table or remove an old table) to aid retrying on failure
1. Keeps the GitLab code base clean and well structured 1. Keeps the GitLab code base clean and well structured
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
...@@ -140,6 +151,7 @@ Please ensure you support the feature you contribute through all of these steps. ...@@ -140,6 +151,7 @@ Please ensure you support the feature you contribute through all of these steps.
1. [CoffeeScript](https://github.com/thoughtbot/guides/tree/master/style#coffeescript) 1. [CoffeeScript](https://github.com/thoughtbot/guides/tree/master/style#coffeescript)
1. [Shell commands](doc/development/shell_commands.md) created by GitLab contributors to enhance security 1. [Shell commands](doc/development/shell_commands.md) created by GitLab contributors to enhance security
1. [Markdown](http://www.cirosantilli.com/markdown-styleguide) 1. [Markdown](http://www.cirosantilli.com/markdown-styleguide)
1. Interface text should be written subjectively instead of objectively. It should be the gitlab core team addressing a person. It should be written in present time and never use past tense (has been/was). For example instead of "prohibited this user from being saved due to the following errors:" the text should be "sorry, we could not create your account because:". Also these [excellent writing guidelines](https://github.com/NARKOZ/guides#writing).
This is also the style used by linting tools such as [RuboCop](https://github.com/bbatsov/rubocop), [PullReview](https://www.pullreview.com/) and [Hound CI](https://houndci.com). This is also the style used by linting tools such as [RuboCop](https://github.com/bbatsov/rubocop), [PullReview](https://www.pullreview.com/) and [Hound CI](https://houndci.com).
......
...@@ -32,10 +32,15 @@ gem 'omniauth-twitter' ...@@ -32,10 +32,15 @@ gem 'omniauth-twitter'
gem 'omniauth-github' gem 'omniauth-github'
gem 'omniauth-shibboleth' gem 'omniauth-shibboleth'
gem 'omniauth-kerberos' gem 'omniauth-kerberos'
gem 'doorkeeper', '2.1.0'
gem "rack-oauth2", "~> 1.0.5"
# Browser detection
gem "browser"
# 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.rc12' gem "gitlab_git", '7.0.0.rc14'
# 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'
...@@ -93,7 +98,7 @@ gem "github-markup" ...@@ -93,7 +98,7 @@ gem "github-markup"
gem 'redcarpet', '~> 3.1.2' gem 'redcarpet', '~> 3.1.2'
gem 'RedCloth' gem 'RedCloth'
gem 'rdoc', '~>3.6' gem 'rdoc', '~>3.6'
gem 'org-ruby', '= 0.9.9' gem 'org-ruby', '= 0.9.12'
gem 'creole', '~>0.3.6' gem 'creole', '~>0.3.6'
gem 'wikicloth', '=0.8.1' gem 'wikicloth', '=0.8.1'
gem 'asciidoctor', '= 0.1.4' gem 'asciidoctor', '= 0.1.4'
...@@ -173,7 +178,6 @@ gem 'semantic-ui-sass', '~> 0.16.1.0' ...@@ -173,7 +178,6 @@ gem 'semantic-ui-sass', '~> 0.16.1.0'
gem "sass-rails", '~> 4.0.2' gem "sass-rails", '~> 4.0.2'
gem "coffee-rails" gem "coffee-rails"
gem "uglifier" gem "uglifier"
gem "therubyracer"
gem 'turbolinks' gem 'turbolinks'
gem 'jquery-turbolinks' gem 'jquery-turbolinks'
...@@ -258,6 +262,9 @@ end ...@@ -258,6 +262,9 @@ end
group :production do group :production do
gem "gitlab_meta", '7.0' gem "gitlab_meta", '7.0'
gem "therubyracer"
end end
gem "newrelic_rpm" gem "newrelic_rpm"
gem 'octokit', '3.7.0'
...@@ -37,6 +37,7 @@ GEM ...@@ -37,6 +37,7 @@ GEM
rake (>= 0.8.7) rake (>= 0.8.7)
arel (5.0.1.20140414130214) arel (5.0.1.20140414130214)
asciidoctor (0.1.4) asciidoctor (0.1.4)
attr_required (1.0.0)
awesome_print (1.2.0) awesome_print (1.2.0)
axiom-types (0.0.5) axiom-types (0.0.5)
descendants_tracker (~> 0.0.1) descendants_tracker (~> 0.0.1)
...@@ -49,6 +50,7 @@ GEM ...@@ -49,6 +50,7 @@ GEM
debug_inspector (>= 0.0.1) debug_inspector (>= 0.0.1)
bootstrap-sass (3.0.3.0) bootstrap-sass (3.0.3.0)
sass (~> 3.2) sass (~> 3.2)
browser (0.7.2)
builder (3.2.2) builder (3.2.2)
capybara (2.2.1) capybara (2.2.1)
mime-types (>= 1.16) mime-types (>= 1.16)
...@@ -107,6 +109,8 @@ GEM ...@@ -107,6 +109,8 @@ GEM
diff-lcs (1.2.5) diff-lcs (1.2.5)
diffy (3.0.3) diffy (3.0.3)
docile (1.1.5) docile (1.1.5)
doorkeeper (2.1.0)
railties (>= 3.1)
dotenv (0.9.0) dotenv (0.9.0)
dropzonejs-rails (0.4.14) dropzonejs-rails (0.4.14)
rails (> 3.1) rails (> 3.1)
...@@ -120,7 +124,7 @@ GEM ...@@ -120,7 +124,7 @@ GEM
equalizer (0.0.8) equalizer (0.0.8)
erubis (2.7.0) erubis (2.7.0)
escape_utils (0.2.4) escape_utils (0.2.4)
eventmachine (1.0.3) eventmachine (1.0.4)
excon (0.32.1) excon (0.32.1)
execjs (2.0.2) execjs (2.0.2)
expression_parser (0.9.0) expression_parser (0.9.0)
...@@ -158,7 +162,7 @@ GEM ...@@ -158,7 +162,7 @@ GEM
dotenv (>= 0.7) dotenv (>= 0.7)
thor (>= 0.13.6) thor (>= 0.13.6)
formatador (0.2.4) formatador (0.2.4)
gemnasium-gitlab-service (0.2.2) gemnasium-gitlab-service (0.2.3)
rugged (~> 0.19) rugged (~> 0.19)
gherkin-ruby (0.3.1) gherkin-ruby (0.3.1)
racc racc
...@@ -179,7 +183,7 @@ GEM ...@@ -179,7 +183,7 @@ 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.rc12) gitlab_git (7.0.0.rc14)
activesupport (~> 4.0) activesupport (~> 4.0)
charlock_holmes (~> 0.6) charlock_holmes (~> 0.6)
gitlab-linguist (~> 3.0) gitlab-linguist (~> 3.0)
...@@ -250,6 +254,7 @@ GEM ...@@ -250,6 +254,7 @@ GEM
json (~> 1.8) json (~> 1.8)
multi_xml (>= 0.5.2) multi_xml (>= 0.5.2)
httpauth (0.2.1) httpauth (0.2.1)
httpclient (2.5.3.3)
i18n (0.6.11) i18n (0.6.11)
ice_nine (0.10.0) ice_nine (0.10.0)
jalalidate (0.3.3) jalalidate (0.3.3)
...@@ -276,7 +281,7 @@ GEM ...@@ -276,7 +281,7 @@ GEM
kaminari (0.15.1) kaminari (0.15.1)
actionpack (>= 3.0.0) actionpack (>= 3.0.0)
activesupport (>= 3.0.0) activesupport (>= 3.0.0)
kgio (2.8.1) kgio (2.9.2)
launchy (2.4.2) launchy (2.4.2)
addressable (~> 2.3) addressable (~> 2.3)
letter_opener (1.1.2) letter_opener (1.1.2)
...@@ -314,6 +319,8 @@ GEM ...@@ -314,6 +319,8 @@ GEM
jwt (~> 0.1.4) jwt (~> 0.1.4)
multi_json (~> 1.0) multi_json (~> 1.0)
rack (~> 1.2) rack (~> 1.2)
octokit (3.7.0)
sawyer (~> 0.6.0, >= 0.5.3)
omniauth (1.1.4) omniauth (1.1.4)
hashie (>= 1.2, < 3) hashie (>= 1.2, < 3)
rack rack
...@@ -339,7 +346,7 @@ GEM ...@@ -339,7 +346,7 @@ GEM
omniauth-twitter (1.0.1) omniauth-twitter (1.0.1)
multi_json (~> 1.3) multi_json (~> 1.3)
omniauth-oauth (~> 1.0) omniauth-oauth (~> 1.0)
org-ruby (0.9.9) org-ruby (0.9.12)
rubypants (~> 0.2) rubypants (~> 0.2)
orm_adapter (0.5.0) orm_adapter (0.5.0)
pg (0.15.1) pg (0.15.1)
...@@ -362,13 +369,19 @@ GEM ...@@ -362,13 +369,19 @@ GEM
rack (1.5.2) rack (1.5.2)
rack-accept (0.4.5) rack-accept (0.4.5)
rack (>= 0.4) rack (>= 0.4)
rack-attack (2.3.0) rack-attack (4.2.0)
rack rack
rack-cors (0.2.9) rack-cors (0.2.9)
rack-mini-profiler (0.9.0) rack-mini-profiler (0.9.0)
rack (>= 1.1.3) rack (>= 1.1.3)
rack-mount (0.8.3) rack-mount (0.8.3)
rack (>= 1.0.0) rack (>= 1.0.0)
rack-oauth2 (1.0.8)
activesupport (>= 2.3)
attr_required (>= 0.0.5)
httpclient (>= 2.2.0.2)
multi_json (>= 1.3.6)
rack (>= 1.1)
rack-protection (1.5.1) rack-protection (1.5.1)
rack rack
rack-test (0.6.2) rack-test (0.6.2)
...@@ -399,7 +412,7 @@ GEM ...@@ -399,7 +412,7 @@ GEM
activesupport (= 4.1.1) activesupport (= 4.1.1)
rake (>= 0.8.7) rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0) thor (>= 0.18.1, < 2.0)
raindrops (0.12.0) raindrops (0.13.0)
rake (10.3.2) rake (10.3.2)
raphael-rails (2.1.2) raphael-rails (2.1.2)
rb-fsevent (0.9.3) rb-fsevent (0.9.3)
...@@ -462,6 +475,9 @@ GEM ...@@ -462,6 +475,9 @@ GEM
sass (~> 3.2.0) sass (~> 3.2.0)
sprockets (~> 2.8, <= 2.11.0) sprockets (~> 2.8, <= 2.11.0)
sprockets-rails (~> 2.0) sprockets-rails (~> 2.0)
sawyer (0.6.0)
addressable (~> 2.3.5)
faraday (~> 0.8, < 0.10)
sdoc (0.3.20) sdoc (0.3.20)
json (>= 1.1.3) json (>= 1.1.3)
rdoc (~> 3.10) rdoc (~> 3.10)
...@@ -605,6 +621,7 @@ DEPENDENCIES ...@@ -605,6 +621,7 @@ DEPENDENCIES
better_errors better_errors
binding_of_caller binding_of_caller
bootstrap-sass (~> 3.0) bootstrap-sass (~> 3.0)
browser
capybara (~> 2.2.1) capybara (~> 2.2.1)
carrierwave carrierwave
coffee-rails coffee-rails
...@@ -617,6 +634,7 @@ DEPENDENCIES ...@@ -617,6 +634,7 @@ DEPENDENCIES
devise (= 3.2.4) devise (= 3.2.4)
devise-async (= 0.9.0) devise-async (= 0.9.0)
diffy (~> 3.0.3) diffy (~> 3.0.3)
doorkeeper (= 2.1.0)
dropzonejs-rails dropzonejs-rails
email_spec email_spec
enumerize enumerize
...@@ -631,7 +649,7 @@ DEPENDENCIES ...@@ -631,7 +649,7 @@ 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.rc12) gitlab_git (= 7.0.0.rc14)
gitlab_meta (= 7.0) gitlab_meta (= 7.0)
gitlab_omniauth-ldap (= 1.2.0) gitlab_omniauth-ldap (= 1.2.0)
gollum-lib (~> 3.0.0) gollum-lib (~> 3.0.0)
...@@ -660,13 +678,14 @@ DEPENDENCIES ...@@ -660,13 +678,14 @@ DEPENDENCIES
mysql2 mysql2
newrelic_rpm newrelic_rpm
nprogress-rails nprogress-rails
octokit (= 3.7.0)
omniauth (~> 1.1.3) omniauth (~> 1.1.3)
omniauth-github omniauth-github
omniauth-google-oauth2 omniauth-google-oauth2
omniauth-kerberos omniauth-kerberos
omniauth-shibboleth omniauth-shibboleth
omniauth-twitter omniauth-twitter
org-ruby (= 0.9.9) org-ruby (= 0.9.12)
pg pg
poltergeist (~> 1.5.1) poltergeist (~> 1.5.1)
pry pry
...@@ -674,6 +693,7 @@ DEPENDENCIES ...@@ -674,6 +693,7 @@ DEPENDENCIES
rack-attack rack-attack
rack-cors rack-cors
rack-mini-profiler rack-mini-profiler
rack-oauth2 (~> 1.0.5)
rails (~> 4.1.0) rails (~> 4.1.0)
rails_autolink (~> 1.1) rails_autolink (~> 1.1)
rails_best_practices rails_best_practices
......
web: bundle exec unicorn_rails -p ${PORT:="3000"} -E ${RAILS_ENV:="development"} -c ${UNICORN_CONFIG:="config/unicorn.rb"} web: bundle exec unicorn_rails -p ${PORT:="3000"} -E ${RAILS_ENV:="development"} -c ${UNICORN_CONFIG:="config/unicorn.rb"}
worker: bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,common,default,gitlab_shell worker: bundle exec sidekiq -q post_receive -q mailer -q system_hook -q project_web_hook -q gitlab_shell -q common -q default
...@@ -11,6 +11,14 @@ ...@@ -11,6 +11,14 @@
- Completely free and open source (MIT Expat license) - Completely free and open source (MIT Expat license)
- Powered by Ruby on Rails - Powered by Ruby on Rails
## Editions
There are two editions of GitLab.
GitLab [Community Edition](https://about.gitlab.com/features/) (CE) is available without any costs under an MIT license.
GitLab Enterprise Edition (EE) includes [extra features](https://about.gitlab.com/features/#compare) that are most useful for organizations with more than 100 users.
To get access to the EE and support please [become a subscriber](https://about.gitlab.com/pricing/).
## Canonical source ## Canonical source
- The source of GitLab Community Edition is [hosted on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/) and there are mirrors to make [contributing](CONTRIBUTING.md) as easy as possible. - The source of GitLab Community Edition is [hosted on GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/) and there are mirrors to make [contributing](CONTRIBUTING.md) as easy as possible.
......
7.6.1 7.7.1
\ No newline at end of file \ No newline at end of file
...@@ -12,7 +12,7 @@ class @Activities ...@@ -12,7 +12,7 @@ class @Activities
toggleFilter: (sender) -> toggleFilter: (sender) ->
sender.parent().toggleClass "inactive" sender.parent().toggleClass "active"
event_filters = $.cookie("event_filter") event_filters = $.cookie("event_filter")
filter = sender.attr("id").split("_")[0] filter = sender.attr("id").split("_")[0]
if event_filters if event_filters
......
@Api = @Api =
groups_path: "/api/:version/groups.json"
group_path: "/api/:version/groups/:id.json"
users_path: "/api/:version/users.json" users_path: "/api/:version/users.json"
user_path: "/api/:version/users/:id.json" user_path: "/api/:version/users/:id.json"
notes_path: "/api/:version/projects/:id/notes.json" notes_path: "/api/:version/projects/:id/notes.json"
...@@ -51,6 +53,33 @@ ...@@ -51,6 +53,33 @@
).done (users) -> ).done (users) ->
callback(users) callback(users)
group: (group_id, callback) ->
url = Api.buildUrl(Api.group_path)
url = url.replace(':id', group_id)
$.ajax(
url: url
data:
private_token: gon.api_token
dataType: "json"
).done (group) ->
callback(group)
# Return groups list. Filtered by query
# Only active groups retrieved
groups: (query, skip_ldap, callback) ->
url = Api.buildUrl(Api.groups_path)
$.ajax(
url: url
data:
private_token: gon.api_token
search: query
per_page: 20
dataType: "json"
).done (groups) ->
callback(groups)
# Return project users list. Filtered by query # Return project users list. Filtered by query
# Only active users retrieved # Only active users retrieved
projectUsers: (project_id, query, callback) -> projectUsers: (project_id, query, callback) ->
......
...@@ -18,7 +18,6 @@ ...@@ -18,7 +18,6 @@
#= 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
...@@ -56,12 +55,6 @@ window.ajaxGet = (url) -> ...@@ -56,12 +55,6 @@ window.ajaxGet = (url) ->
window.showAndHide = (selector) -> window.showAndHide = (selector) ->
window.errorMessage = (message) ->
ehtml = $("<p>")
ehtml.addClass("error_message")
ehtml.html(message)
ehtml
window.split = (val) -> window.split = (val) ->
return val.split( /,\s*/ ) return val.split( /,\s*/ )
...@@ -120,9 +113,19 @@ window.unbindEvents = -> ...@@ -120,9 +113,19 @@ window.unbindEvents = ->
$(document).unbind('scroll') $(document).unbind('scroll')
$(document).off('scroll') $(document).off('scroll')
window.shiftWindow = ->
scrollBy 0, -50
document.addEventListener("page:fetch", unbindEvents) document.addEventListener("page:fetch", unbindEvents)
# Scroll the window to avoid the topnav bar
# https://github.com/twitter/bootstrap/issues/1768
if location.hash
setTimeout shiftWindow, 1
window.addEventListener "hashchange", shiftWindow
$ -> $ ->
# Click a .one_click_select field, select the contents # Click a .one_click_select field, select the contents
$(".one_click_select").on 'click', -> $(@).select() $(".one_click_select").on 'click', -> $(@).select()
......
...@@ -33,17 +33,20 @@ class Dispatcher ...@@ -33,17 +33,20 @@ class Dispatcher
GitLab.GfmAutoComplete.setup() GitLab.GfmAutoComplete.setup()
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
new ZenMode() new ZenMode()
new DropzoneInput($('.issue-form'))
when 'projects:merge_requests:new', 'projects:merge_requests:edit' when 'projects:merge_requests:new', 'projects:merge_requests:edit'
GitLab.GfmAutoComplete.setup() GitLab.GfmAutoComplete.setup()
new Diff() new Diff()
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
new ZenMode() new ZenMode()
new DropzoneInput($('.merge-request-form'))
when 'projects:merge_requests:show' when 'projects:merge_requests:show'
new Diff() new Diff()
shortcut_handler = new ShortcutsIssueable() shortcut_handler = new ShortcutsIssueable()
new ZenMode() new ZenMode()
when "projects:merge_requests:diffs" when "projects:merge_requests:diffs"
new Diff() new Diff()
new ZenMode()
when 'projects:merge_requests:index' when 'projects:merge_requests:index'
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
when 'dashboard:show' when 'dashboard:show'
...@@ -108,6 +111,7 @@ class Dispatcher ...@@ -108,6 +111,7 @@ class Dispatcher
new Wikis() new Wikis()
shortcut_handler = new ShortcutsNavigation() shortcut_handler = new ShortcutsNavigation()
new ZenMode() new ZenMode()
new DropzoneInput($('.wiki-form'))
when 'snippets', 'labels', 'graphs' when 'snippets', 'labels', 'graphs'
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'
......
class @DropzoneInput
constructor: (form) ->
Dropzone.autoDiscover = false
alertClass = "alert alert-danger alert-dismissable div-dropzone-alert"
alertAttr = "class=\"close\" data-dismiss=\"alert\"" + "aria-hidden=\"true\""
divHover = "<div class=\"div-dropzone-hover\"></div>"
divSpinner = "<div class=\"div-dropzone-spinner\"></div>"
divAlert = "<div class=\"" + alertClass + "\"></div>"
iconPicture = "<i class=\"fa fa-picture-o div-dropzone-icon\"></i>"
iconSpinner = "<i class=\"fa fa-spinner fa-spin div-dropzone-icon\"></i>"
btnAlert = "<button type=\"button\"" + alertAttr + ">&times;</button>"
project_image_path_upload = window.project_image_path_upload or null
form_textarea = $(form).find("textarea.markdown-area")
form_textarea.wrap "<div class=\"div-dropzone\"></div>"
form_dropzone = $(form).find('.div-dropzone')
form_dropzone.parent().addClass "div-dropzone-wrapper"
form_dropzone.append divHover
$(".div-dropzone-hover").append iconPicture
form_dropzone.append divSpinner
$(".div-dropzone-spinner").append iconSpinner
$(".div-dropzone-spinner").css
"opacity": 0
"display": "none"
# Preview button
$(document).off "click", ".js-md-preview-button"
$(document).on "click", ".js-md-preview-button", (e) ->
###
Shows the Markdown preview.
Lets the server render GFM into Html and displays it.
###
e.preventDefault()
form = $(this).closest("form")
# toggle tabs
form.find(".js-md-write-button").parent().removeClass "active"
form.find(".js-md-preview-button").parent().addClass "active"
# toggle content
form.find(".md-write-holder").hide()
form.find(".md-preview-holder").show()
preview = form.find(".js-md-preview")
mdText = form.find(".markdown-area").val()
if mdText.trim().length is 0
preview.text "Nothing to preview."
else
preview.text "Loading..."
$.get($(this).data("url"),
md_text: mdText
).success (previewData) ->
preview.html previewData
# Write button
$(document).off "click", ".js-md-write-button"
$(document).on "click", ".js-md-write-button", (e) ->
###
Shows the Markdown textarea.
###
e.preventDefault()
form = $(this).closest("form")
# toggle tabs
form.find(".js-md-write-button").parent().addClass "active"
form.find(".js-md-preview-button").parent().removeClass "active"
# toggle content
form.find(".md-write-holder").show()
form.find(".md-preview-holder").hide()
dropzone = form_dropzone.dropzone(
url: project_image_path_upload
dictDefaultMessage: ""
clickable: true
paramName: "markdown_img"
maxFilesize: 10
uploadMultiple: false
acceptedFiles: "image/jpg,image/jpeg,image/gif,image/png"
headers:
"X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content")
previewContainer: false
processing: ->
$(".div-dropzone-alert").alert "close"
dragover: ->
form_textarea.addClass "div-dropzone-focus"
form.find(".div-dropzone-hover").css "opacity", 0.7
return
dragleave: ->
form_textarea.removeClass "div-dropzone-focus"
form.find(".div-dropzone-hover").css "opacity", 0
return
drop: ->
form_textarea.removeClass "div-dropzone-focus"
form.find(".div-dropzone-hover").css "opacity", 0
form_textarea.focus()
return
success: (header, response) ->
child = $(dropzone[0]).children("textarea")
$(child).val $(child).val() + formatLink(response.link) + "\n"
return
error: (temp, errorMessage) ->
checkIfMsgExists = $(".error-alert").children().length
if checkIfMsgExists is 0
$(".error-alert").append divAlert
$(".div-dropzone-alert").append btnAlert + errorMessage
return
sending: ->
form_dropzone.find(".div-dropzone-spinner").css
"opacity": 0.7
"display": "inherit"
return
complete: ->
$(".dz-preview").remove()
$(".markdown-area").trigger "input"
$(".div-dropzone-spinner").css
"opacity": 0
"display": "none"
return
)
child = $(dropzone[0]).children("textarea")
formatLink = (str) ->
"![" + str.alt + "](" + str.url + ")"
handlePaste = (e) ->
e.preventDefault()
my_event = e.originalEvent
if my_event.clipboardData and my_event.clipboardData.items
processItem(my_event)
processItem = (e) ->
image = isImage(e)
if image
filename = getFilename(e) or "image.png"
text = "{{" + filename + "}}"
pasteText(text)
uploadFile image.getAsFile(), filename
else
text = e.clipboardData.getData("text/plain")
pasteText(text)
isImage = (data) ->
i = 0
while i < data.clipboardData.items.length
item = data.clipboardData.items[i]
if item.type.indexOf("image") isnt -1
return item
i++
return false
pasteText = (text) ->
caretStart = $(child)[0].selectionStart
caretEnd = $(child)[0].selectionEnd
textEnd = $(child).val().length
beforeSelection = $(child).val().substring 0, caretStart
afterSelection = $(child).val().substring caretEnd, textEnd
$(child).val beforeSelection + text + afterSelection
form_textarea.trigger "input"
getFilename = (e) ->
if window.clipboardData and window.clipboardData.getData
value = window.clipboardData.getData("Text")
else if e.clipboardData and e.clipboardData.getData
value = e.clipboardData.getData("text/plain")
value = value.split("\r")
value.first()
uploadFile = (item, filename) ->
formData = new FormData()
formData.append "markdown_img", item, filename
$.ajax
url: project_image_path_upload
type: "POST"
data: formData
dataType: "json"
processData: false
contentType: false
headers:
"X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content")
beforeSend: ->
showSpinner()
closeAlertMessage()
success: (e, textStatus, response) ->
insertToTextArea(filename, formatLink(response.responseJSON.link))
error: (response) ->
showError(response.responseJSON.message)
complete: ->
closeSpinner()
insertToTextArea = (filename, url) ->
$(child).val (index, val) ->
val.replace("{{" + filename + "}}", url + "\n")
appendToTextArea = (url) ->
$(child).val (index, val) ->
val + url + "\n"
showSpinner = (e) ->
form.find(".div-dropzone-spinner").css
"opacity": 0.7
"display": "inherit"
closeSpinner = ->
form.find(".div-dropzone-spinner").css
"opacity": 0
"display": "none"
showError = (message) ->
checkIfMsgExists = $(".error-alert").children().length
if checkIfMsgExists is 0
$(".error-alert").append divAlert
$(".div-dropzone-alert").append btnAlert + message
closeAlertMessage = ->
form.find(".div-dropzone-alert").alert "close"
form.find(".markdown-selector").click (e) ->
e.preventDefault()
$(@).closest('.gfm-form').find('.div-dropzone').click()
return
formatLink: (str) ->
"![" + str.alt + "](" + str.url + ")"
class @GroupsSelect
constructor: ->
$('.ajax-groups-select').each (i, select) =>
skip_ldap = $(select).hasClass('skip_ldap')
$(select).select2
placeholder: "Search for a group"
multiple: $(select).hasClass('multiselect')
minimumInputLength: 0
query: (query) ->
Api.groups query.term, skip_ldap, (groups) ->
data = { results: groups }
query.callback(data)
initSelection: (element, callback) ->
id = $(element).val()
if id isnt ""
Api.group(id, callback)
formatResult: (args...) =>
@formatResult(args...)
formatSelection: (args...) =>
@formatSelection(args...)
dropdownCssClass: "ajax-groups-dropdown"
escapeMarkup: (m) -> # we do not want to escape markup since we are displaying html in results
m
formatResult: (group) ->
if group.avatar_url
avatar = group.avatar_url
else
avatar = gon.default_avatar_url
"<div class='group-result'>
<div class='group-name'>#{group.name}</div>
<div class='group-path'>#{group.path}</div>
</div>"
formatSelection: (group) ->
group.name
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", -> $(".context .inline-update").on "change", "select", ->
$(this).submit() $(this).submit()
$(".issue-box .inline-update").on "change", "#issue_assignee_id", -> $(".context .inline-update").on "change", "#issue_assignee_id", ->
$(this).submit() $(this).submit()
if $("a.btn-close").length if $("a.btn-close").length
......
formatLink = (str) ->
"![" + str.alt + "](" + str.url + ")"
$(document).ready ->
alertClass = "alert alert-danger alert-dismissable div-dropzone-alert"
alertAttr = "class=\"close\" data-dismiss=\"alert\"" + "aria-hidden=\"true\""
divHover = "<div class=\"div-dropzone-hover\"></div>"
divSpinner = "<div class=\"div-dropzone-spinner\"></div>"
divAlert = "<div class=\"" + alertClass + "\"></div>"
iconPicture = "<i class=\"fa fa-picture-o div-dropzone-icon\"></i>"
iconSpinner = "<i class=\"fa fa-spinner fa-spin div-dropzone-icon\"></i>"
btnAlert = "<button type=\"button\"" + alertAttr + ">&times;</button>"
project_image_path_upload = window.project_image_path_upload or null
$("textarea.markdown-area").wrap "<div class=\"div-dropzone\"></div>"
$(".div-dropzone").parent().addClass "div-dropzone-wrapper"
$(".div-dropzone").append divHover
$(".div-dropzone-hover").append iconPicture
$(".div-dropzone").append divSpinner
$(".div-dropzone-spinner").append iconSpinner
$(".div-dropzone-spinner").css
"opacity": 0
"display": "none"
# Preview button
$(document).off "click", ".js-md-preview-button"
$(document).on "click", ".js-md-preview-button", (e) ->
###
Shows the Markdown preview.
Lets the server render GFM into Html and displays it.
###
e.preventDefault()
form = $(this).closest("form")
# toggle tabs
form.find(".js-md-write-button").parent().removeClass "active"
form.find(".js-md-preview-button").parent().addClass "active"
# toggle content
form.find(".md-write-holder").hide()
form.find(".md-preview-holder").show()
preview = form.find(".js-md-preview")
mdText = form.find(".markdown-area").val()
if mdText.trim().length is 0
preview.text "Nothing to preview."
else
preview.text "Loading..."
$.get($(this).data("url"),
md_text: mdText
).success (previewData) ->
preview.html previewData
# Write button
$(document).off "click", ".js-md-write-button"
$(document).on "click", ".js-md-write-button", (e) ->
###
Shows the Markdown textarea.
###
e.preventDefault()
form = $(this).closest("form")
# toggle tabs
form.find(".js-md-write-button").parent().addClass "active"
form.find(".js-md-preview-button").parent().removeClass "active"
# toggle content
form.find(".md-write-holder").show()
form.find(".md-preview-holder").hide()
dropzone = $(".div-dropzone").dropzone(
url: project_image_path_upload
dictDefaultMessage: ""
clickable: true
paramName: "markdown_img"
maxFilesize: 10
uploadMultiple: false
acceptedFiles: "image/jpg,image/jpeg,image/gif,image/png"
headers:
"X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content")
previewContainer: false
processing: ->
$(".div-dropzone-alert").alert "close"
dragover: ->
$(".div-dropzone > textarea").addClass "div-dropzone-focus"
$(".div-dropzone-hover").css "opacity", 0.7
return
dragleave: ->
$(".div-dropzone > textarea").removeClass "div-dropzone-focus"
$(".div-dropzone-hover").css "opacity", 0
return
drop: ->
$(".div-dropzone > textarea").removeClass "div-dropzone-focus"
$(".div-dropzone-hover").css "opacity", 0
$(".div-dropzone > textarea").focus()
return
success: (header, response) ->
child = $(dropzone[0]).children("textarea")
$(child).val $(child).val() + formatLink(response.link) + "\n"
return
error: (temp, errorMessage) ->
checkIfMsgExists = $(".error-alert").children().length
if checkIfMsgExists is 0
$(".error-alert").append divAlert
$(".div-dropzone-alert").append btnAlert + errorMessage
return
sending: ->
$(".div-dropzone-spinner").css
"opacity": 0.7
"display": "inherit"
return
complete: ->
$(".dz-preview").remove()
$(".markdown-area").trigger "input"
$(".div-dropzone-spinner").css
"opacity": 0
"display": "none"
return
)
child = $(dropzone[0]).children("textarea")
formatLink = (str) ->
"![" + str.alt + "](" + str.url + ")"
handlePaste = (e) ->
e.preventDefault()
my_event = e.originalEvent
if my_event.clipboardData and my_event.clipboardData.items
processItem(my_event)
processItem = (e) ->
image = isImage(e)
if image
filename = getFilename(e) or "image.png"
text = "{{" + filename + "}}"
pasteText(text)
uploadFile image.getAsFile(), filename
else
text = e.clipboardData.getData("text/plain")
pasteText(text)
isImage = (data) ->
i = 0
while i < data.clipboardData.items.length
item = data.clipboardData.items[i]
if item.type.indexOf("image") isnt -1
return item
i++
return false
pasteText = (text) ->
caretStart = $(child)[0].selectionStart
caretEnd = $(child)[0].selectionEnd
textEnd = $(child).val().length
beforeSelection = $(child).val().substring 0, caretStart
afterSelection = $(child).val().substring caretEnd, textEnd
$(child).val beforeSelection + text + afterSelection
$(".markdown-area").trigger "input"
getFilename = (e) ->
if window.clipboardData and window.clipboardData.getData
value = window.clipboardData.getData("Text")
else if e.clipboardData and e.clipboardData.getData
value = e.clipboardData.getData("text/plain")
value = value.split("\r")
value.first()
uploadFile = (item, filename) ->
formData = new FormData()
formData.append "markdown_img", item, filename
$.ajax
url: project_image_path_upload
type: "POST"
data: formData
dataType: "json"
processData: false
contentType: false
headers:
"X-CSRF-Token": $("meta[name=\"csrf-token\"]").attr("content")
beforeSend: ->
showSpinner()
closeAlertMessage()
success: (e, textStatus, response) ->
insertToTextArea(filename, formatLink(response.responseJSON.link))
error: (response) ->
showError(response.responseJSON.message)
complete: ->
closeSpinner()
insertToTextArea = (filename, url) ->
$(child).val (index, val) ->
val.replace("{{" + filename + "}}", url + "\n")
appendToTextArea = (url) ->
$(child).val (index, val) ->
val + url + "\n"
showSpinner = (e) ->
$(".div-dropzone-spinner").css
"opacity": 0.7
"display": "inherit"
closeSpinner = ->
$(".div-dropzone-spinner").css
"opacity": 0
"display": "none"
showError = (message) ->
checkIfMsgExists = $(".error-alert").children().length
if checkIfMsgExists is 0
$(".error-alert").append divAlert
$(".div-dropzone-alert").append btnAlert + message
closeAlertMessage = ->
$(".div-dropzone-alert").alert "close"
$(".markdown-selector").click (e) ->
e.preventDefault()
$(@).closest('.gfm-form').find('.div-dropzone').click()
return
return
...@@ -26,9 +26,9 @@ class @MergeRequest ...@@ -26,9 +26,9 @@ class @MergeRequest
initContextWidget: -> initContextWidget: ->
$('.edit-merge_request.inline-update input[type="submit"]').hide() $('.edit-merge_request.inline-update input[type="submit"]').hide()
$(".issue-box .inline-update").on "change", "select", -> $(".context .inline-update").on "change", "select", ->
$(this).submit() $(this).submit()
$(".issue-box .inline-update").on "change", "#merge_request_assignee_id", -> $(".context .inline-update").on "change", "#merge_request_assignee_id", ->
$(this).submit() $(this).submit()
initMergeWidget: -> initMergeWidget: ->
...@@ -89,6 +89,9 @@ class @MergeRequest ...@@ -89,6 +89,9 @@ class @MergeRequest
this.$('.merge-request-tabs .diffs-tab').addClass 'active' this.$('.merge-request-tabs .diffs-tab').addClass 'active'
this.loadDiff() unless @diffs_loaded this.loadDiff() unless @diffs_loaded
this.$('.diffs').show() this.$('.diffs').show()
when 'commits'
this.$('.merge-request-tabs .commits-tab').addClass 'active'
this.$('.commits').show()
else else
this.$('.merge-request-tabs .notes-tab').addClass 'active' this.$('.merge-request-tabs .notes-tab').addClass 'active'
this.$('.notes').show() this.$('.notes').show()
...@@ -132,3 +135,16 @@ class @MergeRequest ...@@ -132,3 +135,16 @@ 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()
mergeInProgress: ->
$.ajax
type: 'GET'
url: $('.merge-request').data('url')
success: (data) =>
switch data.state
when 'merged'
location.reload()
else
setTimeout(merge_request.mergeInProgress, 3000)
dataType: 'json'
...@@ -219,6 +219,7 @@ class @Notes ...@@ -219,6 +219,7 @@ class @Notes
setupNoteForm: (form) -> setupNoteForm: (form) ->
disableButtonIfEmptyField form.find(".js-note-text"), form.find(".js-comment-button") disableButtonIfEmptyField form.find(".js-note-text"), form.find(".js-comment-button")
form.removeClass "js-new-note-form" form.removeClass "js-new-note-form"
form.find('.div-dropzone').remove()
# setup preview buttons # setup preview buttons
form.find(".js-md-write-button, .js-md-preview-button").tooltip placement: "left" form.find(".js-md-write-button, .js-md-preview-button").tooltip placement: "left"
...@@ -233,6 +234,7 @@ class @Notes ...@@ -233,6 +234,7 @@ class @Notes
# remove notify commit author checkbox for non-commit notes # remove notify commit author checkbox for non-commit notes
form.find(".js-notify-commit-author").remove() if form.find("#note_noteable_type").val() isnt "Commit" form.find(".js-notify-commit-author").remove() if form.find("#note_noteable_type").val() isnt "Commit"
GitLab.GfmAutoComplete.setup() GitLab.GfmAutoComplete.setup()
new DropzoneInput(form)
form.show() form.show()
...@@ -259,8 +261,10 @@ class @Notes ...@@ -259,8 +261,10 @@ class @Notes
Updates the current note field. Updates the current note field.
### ###
updateNote: (xhr, note, status) => updateNote: (xhr, note, status) =>
note_li = $("#note_" + note.id) note_li = $(".note-row-" + note.id)
note_li.replaceWith(note.html) note_li.replaceWith(note.html)
note_li.find('.note-edit-form').hide()
note_li.find('.note-text').show()
code = "#note_" + note.id + " .highlight pre code" code = "#note_" + note.id + " .highlight pre code"
$(code).each (i, e) -> $(code).each (i, e) ->
hljs.highlightBlock(e) hljs.highlightBlock(e)
...@@ -276,11 +280,19 @@ class @Notes ...@@ -276,11 +280,19 @@ class @Notes
e.preventDefault() e.preventDefault()
note = $(this).closest(".note") note = $(this).closest(".note")
note.find(".note-text").hide() note.find(".note-text").hide()
note.find(".note-header").hide()
base_form = note.find(".note-edit-form")
form = base_form.clone().insertAfter(base_form)
form.addClass('current-note-edit-form')
form.find('.div-dropzone').remove()
# Show the attachment delete link # Show the attachment delete link
note.find(".js-note-attachment-delete").show() note.find(".js-note-attachment-delete").show()
# Setup markdown form
GitLab.GfmAutoComplete.setup() GitLab.GfmAutoComplete.setup()
form = note.find(".note-edit-form") new DropzoneInput(form)
form.show() form.show()
textarea = form.find("textarea") textarea = form.find("textarea")
textarea.focus() textarea.focus()
...@@ -295,8 +307,8 @@ class @Notes ...@@ -295,8 +307,8 @@ class @Notes
e.preventDefault() e.preventDefault()
note = $(this).closest(".note") note = $(this).closest(".note")
note.find(".note-text").show() note.find(".note-text").show()
note.find(".js-note-attachment-delete").hide() note.find(".note-header").show()
note.find(".note-edit-form").hide() note.find(".current-note-edit-form").remove()
### ###
Called in response to deleting a note of any kind. Called in response to deleting a note of any kind.
...@@ -375,7 +387,7 @@ class @Notes ...@@ -375,7 +387,7 @@ class @Notes
### ###
addDiffNote: (e) => addDiffNote: (e) =>
e.preventDefault() e.preventDefault()
link = e.target link = e.currentTarget
form = $(".js-new-note-form") form = $(".js-new-note-form")
row = $(link).closest("tr") row = $(link).closest("tr")
nextRow = row.next() nextRow = row.next()
......
#= 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
$ ->
$(":checkbox").change ->
name = $(this).attr("name")
if name == "developers_can_push"
id = $(this).val()
checked = $(this).is(":checked")
url = $(this).data("url")
$.ajax
type: "PUT"
url: url
dataType: "json"
data:
id: id
developers_can_push: checked
success: ->
new Flash("Branch updated.", "notice")
location.reload true
error: ->
new Flash("Failed to update branch!", "alert")
...@@ -46,7 +46,7 @@ class @ContributorsGraph ...@@ -46,7 +46,7 @@ class @ContributorsGraph
class @ContributorsMasterGraph extends ContributorsGraph class @ContributorsMasterGraph extends ContributorsGraph
constructor: (@data) -> constructor: (@data) ->
@width = $('.container').width() - 70 @width = $('.container').width() - 345
@height = 200 @height = 200
@x = null @x = null
@y = null @y = null
...@@ -119,7 +119,7 @@ class @ContributorsMasterGraph extends ContributorsGraph ...@@ -119,7 +119,7 @@ class @ContributorsMasterGraph extends ContributorsGraph
class @ContributorsAuthorGraph extends ContributorsGraph class @ContributorsAuthorGraph extends ContributorsGraph
constructor: (@data) -> constructor: (@data) ->
@width = $('.container').width()/2 - 100 @width = $('.container').width()/2 - 225
@height = 200 @height = 200
@x = null @x = null
@y = null @y = null
......
...@@ -10,7 +10,15 @@ class @ZenMode ...@@ -10,7 +10,15 @@ class @ZenMode
if not @active_checkbox if not @active_checkbox
@scroll_position = window.pageYOffset @scroll_position = window.pageYOffset
$('body').on 'change', '.zennable input[type=checkbox]', (e) => $('body').on 'click', '.zen-enter-link', (e) =>
e.preventDefault()
$(e.currentTarget).closest('.zennable').find('.zen-toggle-comment').prop('checked', true)
$('body').on 'click', '.zen-leave-link', (e) =>
e.preventDefault()
$(e.currentTarget).closest('.zennable').find('.zen-toggle-comment').prop('checked', false)
$('body').on 'change', '.zen-toggle-comment', (e) =>
checkbox = e.currentTarget checkbox = e.currentTarget
if checkbox.checked if checkbox.checked
# Disable other keyboard shortcuts in ZEN mode # Disable other keyboard shortcuts in ZEN mode
...@@ -32,8 +40,6 @@ class @ZenMode ...@@ -32,8 +40,6 @@ class @ZenMode
@active_zen_area = @active_checkbox.parent().find('textarea') @active_zen_area = @active_checkbox.parent().find('textarea')
@active_zen_area.focus() @active_zen_area.focus()
window.location.hash = ZenMode.fullscreen_prefix + @active_checkbox.prop('id') window.location.hash = ZenMode.fullscreen_prefix + @active_checkbox.prop('id')
# Disable dropzone in ZEN mode
Dropzone.forElement('.div-dropzone').disable()
exitZenMode: => exitZenMode: =>
if @active_zen_area isnt null if @active_zen_area isnt null
......
...@@ -207,24 +207,16 @@ li.note { ...@@ -207,24 +207,16 @@ li.note {
} }
} }
.no-ssh-key-message { .browser-alert {
padding: 10px 0; padding: 10px;
background: #C67;
margin: 0;
color: #FFF;
margin-top: -1px;
text-align: center; text-align: center;
background: #C67;
color: #fff;
font-weight: bold;
a { a {
color: #fff; color: #fff;
text-decoration: underline; text-decoration: underline;
} }
.links-xs {
text-align: center;
font-size: 16px;
padding: 5px;
}
} }
.warning_message { .warning_message {
...@@ -281,10 +273,6 @@ img.emoji { ...@@ -281,10 +273,6 @@ img.emoji {
height: 220px; height: 220px;
} }
.navless-container {
margin-top: 20px;
}
.description-block { .description-block {
@extend .light-well; @extend .light-well;
@extend .light; @extend .light;
...@@ -300,11 +288,17 @@ table { ...@@ -300,11 +288,17 @@ table {
.dashboard-intro-icon { .dashboard-intro-icon {
float: left; float: left;
text-align: center;
font-size: 32px; font-size: 32px;
color: #AAA; color: #AAA;
padding: 5px 0; width: 60px;
width: 50px; }
min-height: 100px;
.dashboard-intro-text {
display: inline-block;
margin-left: -60px;
padding-left: 60px;
width: 100%;
} }
.broadcast-message { .broadcast-message {
...@@ -355,3 +349,9 @@ table { ...@@ -355,3 +349,9 @@ table {
.task-status { .task-status {
margin-left: 10px; margin-left: 10px;
} }
#nprogress .spinner {
top: auto !important;
bottom: 20px !important;
left: 20px !important;
}
...@@ -31,7 +31,12 @@ fieldset legend { ...@@ -31,7 +31,12 @@ fieldset legend {
margin-bottom: 18px; margin-bottom: 18px;
background-color: whitesmoke; background-color: whitesmoke;
border-top: 1px solid #e5e5e5; border-top: 1px solid #e5e5e5;
padding-left: 17%; }
@media (min-width: $screen-sm-min) {
.form-actions {
padding-left: 17%;
}
} }
label { label {
...@@ -88,139 +93,7 @@ label { ...@@ -88,139 +93,7 @@ label {
@include box-shadow(none); @include box-shadow(none);
} }
.issuable-description { .issuable-description,
.wiki-content {
margin-top: 35px; margin-top: 35px;
} }
.zennable {
position: relative;
input {
display: none;
}
.collapse {
display: none;
opacity: 0.5;
&:before {
content: '\f066';
font-family: FontAwesome;
color: #000;
font-size: 28px;
position: relative;
padding: 30px 40px 0 0;
}
&:hover {
opacity: 0.8;
}
}
.expand {
opacity: 0.5;
&:before {
content: '\f065';
font-family: FontAwesome;
color: #000;
font-size: 14px;
line-height: 14px;
padding-right: 20px;
position: relative;
vertical-align: middle;
}
&:hover {
opacity: 0.8;
}
}
input:checked ~ .zen-backdrop .expand {
display: none;
}
input:checked ~ .zen-backdrop .collapse {
display: block;
position: absolute;
top: 0;
}
label {
position: absolute;
top: -26px;
right: 0;
font-variant: small-caps;
text-transform: uppercase;
font-size: 10px;
padding: 4px;
font-weight: 500;
letter-spacing: 1px;
&:before {
display: inline-block;
width: 10px;
height: 14px;
}
}
input:checked ~ .zen-backdrop {
background-color: white;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 1031;
textarea {
border: none;
box-shadow: none;
border-radius: 0;
color: #000;
font-size: 20px;
line-height: 26px;
padding: 30px;
display: block;
outline: none;
resize: none;
height: 100vh;
max-width: 900px;
margin: 0 auto;
}
}
.zen-backdrop textarea::-webkit-input-placeholder {
color: white;
}
.zen-backdrop textarea:-moz-placeholder {
color: white;
}
.zen-backdrop textarea::-moz-placeholder {
color: white;
}
.zen-backdrop textarea:-ms-input-placeholder {
color: white;
}
input:checked ~ .zen-backdrop textarea::-webkit-input-placeholder {
color: #999;
}
input:checked ~ .zen-backdrop textarea:-moz-placeholder {
color: #999;
opacity: 1;
}
input:checked ~ .zen-backdrop textarea::-moz-placeholder {
color: #999;
opacity: 1;
}
input:checked ~ .zen-backdrop textarea:-ms-input-placeholder {
color: #999;
}
}
/** /**
* Issue box: * Issue box for showing Open/Closed state:
* Huge block (one per page) for storing title, descripion and other information.
* Used for Issue#show page, MergeRequest#show page etc * Used for Issue#show page, MergeRequest#show page etc
* *
* CLasses:
* .issue-box - Regular box
*/ */
.issue-box { .issue-box {
color: #555; display: inline-block;
margin:20px 0; padding: 7px 13px;
background: $box_bg; font-weight: normal;
@include box-shadow(0 1px 1px rgba(0, 0, 0, 0.09)); margin-right: 5px;
&.issue-box-closed { &.issue-box-closed {
.state { background-color: $bg_danger;
background-color: #F3CECE; color: #FFF;
border-color: $border_danger;
}
.state-label {
background-color: $bg_danger;
color: #FFF;
}
} }
&.issue-box-merged { &.issue-box-merged {
.state { background-color: $bg_primary;
background-color: #B7CEE7; color: #FFF;
border-color: $border_primary;
}
.state-label {
background-color: $bg_primary;
color: #FFF;
}
} }
&.issue-box-open { &.issue-box-open {
.state { background-color: $bg_success;
background-color: #D6F1D7; color: #FFF;
border-color: $bg_success;
}
.state-label {
background-color: $bg_success;
color: #FFF;
}
} }
&.issue-box-expired { &.issue-box-expired {
.state { background: #cea61b;
background-color: #EEE9B3; color: #FFF;
border-color: #faebcc;
}
.state-label {
background: #cea61b;
color: #FFF;
}
}
.control-group {
margin-bottom: 0;
}
.state {
background-color: #f9f9f9;
}
.title {
font-size: 28px;
font-weight: normal;
line-height: 1.5;
margin: 0;
color: #333;
padding: 10px 15px;
}
.context {
border: none;
border-top: 1px solid #eee;
padding: 10px 15px;
// Reset text align for children
.text-right > * { text-align: left; }
@media (max-width: $screen-xs-max) {
// Don't right align on mobile
.text-right { text-align: left; }
.row .col-md-6 {
padding-top: 5px;
}
}
}
.description {
padding: 0 15px 10px 15px;
code {
white-space: pre-wrap;
}
}
.title, .context, .description {
.clearfix {
margin: 0;
}
}
.state-label {
font-size: 14px;
float: left;
font-weight: bold;
padding: 10px 15px;
}
.cross-project-ref {
float: left;
padding: 10px 15px;
}
.creator {
float: right;
padding: 10px 15px;
a {
text-decoration: underline;
}
} }
} }
...@@ -65,6 +65,7 @@ ...@@ -65,6 +65,7 @@
.edit_note, .edit_note,
.issuable-description, .issuable-description,
.milestone-description, .milestone-description,
.wiki-content,
.merge-request-form { .merge-request-form {
.nav-tabs { .nav-tabs {
margin-bottom: 0; margin-bottom: 0;
......
...@@ -116,6 +116,18 @@ select { ...@@ -116,6 +116,18 @@ select {
} }
} }
.group-result {
.group-image {
float: left;
}
.group-name {
font-weight: bold;
}
.group-path {
color: #999;
}
}
.user-result { .user-result {
.user-image { .user-image {
float: left; float: left;
......
table {
&.table {
tr {
td, th {
padding: 8px 10px;
line-height: 20px;
vertical-align: middle;
}
th {
font-weight: normal;
font-size: 15px;
border-bottom: 1px solid #CCC !important;
}
td {
border-color: #F1F1F1 !important;
border-bottom: 1px solid;
}
}
}
}
...@@ -74,6 +74,42 @@ ...@@ -74,6 +74,42 @@
} }
} }
} }
.system-note .timeline-entry-inner {
.timeline-icon {
background: none;
margin-left: 12px;
margin-top: 0;
@include box-shadow(none);
span {
margin: 0 2px;
font-size: 16px;
color: #eeeeee;
}
}
.timeline-content {
background: none;
margin-left: 45px;
padding: 0px 15px;
&:after { border: 0; }
.note-header {
span { font-size: 12px; }
.avatar {
margin-right: 5px;
}
}
.note-text {
font-size: 12px;
margin-left: 20px;
}
}
}
} }
@media (max-width: $screen-xs-max) { @media (max-width: $screen-xs-max) {
......
.zennable {
position: relative;
input {
display: none;
}
.zen-enter-link {
color: #888;
position: absolute;
top: -26px;
right: 4px;
}
.zen-leave-link {
display: none;
color: #888;
position: absolute;
top: 10px;
right: 10px;
padding: 5px;
font-size: 36px;
&:hover {
color: #111;
}
}
input:checked ~ .zen-backdrop .zen-enter-link {
display: none;
}
input:checked ~ .zen-backdrop .zen-leave-link {
display: block;
position: absolute;
top: 0;
}
input:checked ~ .zen-backdrop {
background-color: white;
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 1031;
textarea {
border: none;
box-shadow: none;
border-radius: 0;
color: #000;
font-size: 20px;
line-height: 26px;
padding: 30px;
display: block;
outline: none;
resize: none;
height: 100vh;
max-width: 900px;
margin: 0 auto;
}
}
.zen-backdrop textarea::-webkit-input-placeholder {
color: white;
}
.zen-backdrop textarea:-moz-placeholder {
color: white;
}
.zen-backdrop textarea::-moz-placeholder {
color: white;
}
.zen-backdrop textarea:-ms-input-placeholder {
color: white;
}
input:checked ~ .zen-backdrop textarea::-webkit-input-placeholder {
color: #999;
}
input:checked ~ .zen-backdrop textarea:-moz-placeholder {
color: #999;
opacity: 1;
}
input:checked ~ .zen-backdrop textarea::-moz-placeholder {
color: #999;
opacity: 1;
}
input:checked ~ .zen-backdrop textarea:-ms-input-placeholder {
color: #999;
}
}
...@@ -148,6 +148,10 @@ $list-group-active-bg: $bg_primary; ...@@ -148,6 +148,10 @@ $list-group-active-bg: $bg_primary;
color: #666; color: #666;
} }
.nav-compact > li > a {
padding: 6px 12px;
}
.nav-small > li > a { .nav-small > li > a {
padding: 3px 5px; padding: 3px 5px;
font-size: 12px; font-size: 12px;
......
...@@ -2,10 +2,10 @@ html { ...@@ -2,10 +2,10 @@ html {
overflow-y: scroll; overflow-y: scroll;
&.touch .tooltip { display: none !important; } &.touch .tooltip { display: none !important; }
}
body { body {
padding-bottom: 20px; padding-top: 47px;
}
} }
.container { .container {
...@@ -17,3 +17,6 @@ body { ...@@ -17,3 +17,6 @@ body {
margin: 0 0; margin: 0 0;
} }
.navless-container {
margin-top: 30px;
}
...@@ -65,10 +65,6 @@ ...@@ -65,10 +65,6 @@
max-width: 100%; max-width: 100%;
} }
*:first-child {
margin-top: 0;
}
code { padding: 0 4px; } code { padding: 0 4px; }
h1 { h1 {
......
...@@ -44,6 +44,6 @@ $added: #63c363; ...@@ -44,6 +44,6 @@ $added: #63c363;
$deleted: #f77; $deleted: #f77;
/** /**
* * NProgress customize
*/ */
$nprogress-color: #3498db; $nprogress-color: #c0392b;
...@@ -23,20 +23,6 @@ ...@@ -23,20 +23,6 @@
} }
} }
.dashboard {
.dash-filter {
width: 205px;
float: left;
height: inherit;
}
}
@media (max-width: 1200px) {
.dashboard .dash-filter {
width: 140px;
}
}
.dash-sidebar-tabs { .dash-sidebar-tabs {
margin-bottom: 2px; margin-bottom: 2px;
border: none; border: none;
......
...@@ -140,43 +140,17 @@ ...@@ -140,43 +140,17 @@
} }
} }
/**
* Event filter
*
*/
.event_filter {
position: absolute;
width: 40px;
margin-left: -55px;
.filter_icon {
a {
text-align:center;
background: $bg_primary;
margin-bottom: 10px;
float: left;
padding: 9px 6px;
font-size: 18px;
width: 40px;
color: #FFF;
@include border-radius(3px);
}
&.inactive {
a {
color: #DDD;
background: #f9f9f9;
}
}
}
}
/* /*
* Last push widget * Last push widget
*/ */
.event-last-push { .event-last-push {
overflow: auto;
.event-last-push-text { .event-last-push-text {
@include str-truncated(75%); @include str-truncated(100%);
float:left;
margin-right: -150px;
padding-right: 150px;
line-height: 24px; line-height: 24px;
} }
} }
...@@ -203,3 +177,7 @@ ...@@ -203,3 +177,7 @@
} }
} }
} }
.event_filter li a {
padding: 5px 10px;
}
...@@ -4,9 +4,11 @@ ...@@ -4,9 +4,11 @@
*/ */
header { header {
&.navbar-gitlab { &.navbar-gitlab {
z-index: 100;
margin-bottom: 0; margin-bottom: 0;
min-height: 40px; min-height: 40px;
border: none; border: none;
width: 100%;
.navbar-inner { .navbar-inner {
filter: none; filter: none;
...@@ -52,8 +54,6 @@ header { ...@@ -52,8 +54,6 @@ header {
border-width: 0; border-width: 0;
font-size: 18px; font-size: 18px;
.app_logo { margin-left: -15px; }
.title { .title {
@include str-truncated(70%); @include str-truncated(70%);
} }
...@@ -84,7 +84,10 @@ header { ...@@ -84,7 +84,10 @@ header {
} }
} }
z-index: 10; .container {
width: 100% !important;
padding-left: 0px;
}
/** /**
* *
...@@ -232,21 +235,6 @@ header { ...@@ -232,21 +235,6 @@ header {
color: #fff; color: #fff;
} }
} }
.app_logo {
.separator {
margin-left: 0;
margin-right: 0;
}
}
.separator {
float: left;
height: 46px;
width: 2px;
margin-left: 10px;
margin-right: 10px;
}
} }
.search .search-input { .search .search-input {
......
...@@ -162,3 +162,7 @@ form.edit-issue { ...@@ -162,3 +162,7 @@ form.edit-issue {
} }
} }
} }
.issue-title {
margin-top: 0;
}
/* Login Page */ /* Login Page */
.login-page { .login-page {
h1 { .container {
font-size: 3em; max-width: 960px;
font-weight: 200;
} }
.login-box{ .navbar-gitlab .container {
padding: 0 15px; max-width: none;
}
.login-heading h3 { .brand-holder {
font-weight: 300; font-size: 18px;
line-height: 2; line-height: 1.5;
}
.login-footer { p {
margin-top: 10px; color: #888;
} }
.btn { h1:first-child {
padding: 12px !important; font-weight: normal;
@extend .btn-block; margin-bottom: 30px;
} }
}
.brand-image {
img { img {
max-width: 100%; max-width: 100%;
margin-bottom: 20px; margin-bottom: 30px;
} }
&.default-brand-image { a {
margin: 0 80px; font-weight: bold;
} }
} }
.login-logo { .login-box{
margin: 10px 0 30px 0; background: #fafafa;
display: block; border-radius: 10px;
box-shadow: 0 0px 2px #CCC;
padding: 15px;
.login-heading h3 {
font-weight: 300;
line-height: 1.5;
margin: 0;
display: none;
}
.login-footer {
margin-top: 10px;
}
a.forgot {
float: right;
padding-top: 6px
}
.nav .active a {
background: transparent;
}
} }
.form-control { .form-control {
background-color: #F5F5F5; font-size: 14px;
font-size: 16px; padding: 10px 8px;
padding: 14px 10px;
width: 100%; width: 100%;
height: auto; height: auto;
...@@ -68,11 +86,6 @@ ...@@ -68,11 +86,6 @@
} }
} }
.login-box a.forgot {
float: right;
padding-top: 6px
}
.devise-errors { .devise-errors {
h2 { h2 {
font-size: 14px; font-size: 14px;
...@@ -80,7 +93,19 @@ ...@@ -80,7 +93,19 @@
} }
} }
.brand-holder { .remember-me {
border-right: 1px solid #EEE; margin-top: -10px;
label {
font-weight: normal;
}
}
}
@media (max-width: $screen-xs-max) {
.login-page {
.col-sm-5.pull-right {
float: none !important;
}
} }
} }
.markdown-area {
background: #FFF;
border: 1px solid #ddd;
min-height: 100px;
padding: 5px;
font-size: 14px;
box-shadow: none;
width: 100%;
}
...@@ -11,25 +11,40 @@ ...@@ -11,25 +11,40 @@
} }
} }
.accept-group { .accept-merge-holder {
label { margin-top: 5px;
margin: 5px;
.accept-action {
display: inline-block;
.accept_merge_request {
padding: 10px 20px;
}
}
.accept-control {
display: inline-block;
margin-left: 20px; margin-left: 20px;
padding: 10px 0;
line-height: 20px;
font-weight: bold;
.remove_source_checkbox {
margin: 0;
}
} }
} }
} }
.merge-request .merge-request-tabs{ @media(min-width: $screen-sm-max) {
border-bottom: 2px solid $border_primary; .merge-request .merge-request-tabs{
margin: 20px 0; margin: 20px 0;
li { li {
a { a {
padding: 15px 40px; padding: 15px 40px;
font-size: 14px; font-size: 14px;
margin-bottom: -2px; }
border-bottom: 2px solid $border_primary;
@include border-radius(0px);
} }
} }
} }
...@@ -106,6 +121,7 @@ ...@@ -106,6 +121,7 @@
.mr-state-widget { .mr-state-widget {
background: $box_bg; background: $box_bg;
margin-bottom: 20px; margin-bottom: 20px;
color: #666;
@include box-shadow(0 1px 1px rgba(0, 0, 0, 0.09)); @include box-shadow(0 1px 1px rgba(0, 0, 0, 0.09));
.ci_widget { .ci_widget {
...@@ -150,7 +166,6 @@ ...@@ -150,7 +166,6 @@
padding: 10px 15px; padding: 10px 15px;
h4 { h4 {
font-size: 20px;
font-weight: normal; font-weight: normal;
} }
...@@ -172,7 +187,3 @@ ...@@ -172,7 +187,3 @@
.merge-request-show-labels .label { .merge-request-show-labels .label {
padding: 6px 10px; padding: 6px 10px;
} }
.mr-commits .commit {
padding: 10px 15px;
}
.main-nav {
background: #f5f5f5;
margin: 20px 0;
margin-top: 0;
padding-top: 4px;
border-bottom: 1px solid #E9E9E9;
ul {
padding: 0;
margin: auto;
.count {
font-weight: normal;
display: inline-block;
height: 15px;
padding: 1px 6px;
height: auto;
font-size: 0.82em;
line-height: 14px;
text-align: center;
color: #777;
background: #eee;
@include border-radius(8px);
}
.label {
background: $hover;
text-shadow: none;
color: $style_color;
}
li {
list-style-type: none;
margin: 0;
display: table-cell;
width: 1%;
&.active {
a {
color: $link_color;
font-weight: bold;
border-bottom: 3px solid $link_color;
}
}
&:hover {
a {
color: $link_hover_color;
border-bottom: 3px solid $link_hover_color;
}
}
}
a {
display: block;
text-align: center;
font-weight: bold;
height: 42px;
line-height: 39px;
color: #777;
text-shadow: 0 1px 1px white;
text-decoration: none;
overflow: hidden;
margin-bottom: -1px;
}
}
@media (max-width: $screen-xs-max) {
font-size: 18px;
margin: 0;
max-height: none;
&, .container {
padding: 0;
border-top: 0;
}
ul {
height: auto;
li {
display: list-item;
width: auto;
padding: 5px 0;
&.active {
background-color: $link_hover_color;
a {
color: #fff;
font-weight: normal;
text-shadow: none;
border: none;
&:after { display: none; }
}
}
}
}
}
}
.page-with-sidebar {
background: #F5F5F5;
.sidebar-wrapper {
position: fixed;
top: 0;
left: 0;
height: 100%;
border-right: 1px solid #EAEAEA;
}
}
.sidebar-wrapper {
z-index: 99;
overflow-y: auto;
background: #F5F5F5;
}
.content-wrapper {
width: 100%;
padding: 15px;
background: #FFF;
}
.nav-sidebar {
margin: 0;
list-style: none;
&.navbar-collapse {
padding: 0px !important;
}
}
.nav-sidebar li a .count {
float: right;
background: #eee;
padding: 0px 8px;
@include border-radius(6px);
}
.nav-sidebar li {
&.active a {
color: #111;
background: #EEE;
font-weight: bold;
&.no-highlight {
background: none;
}
i {
color: #444;
}
}
}
.nav-sidebar li {
&.separate-item {
border-top: 1px solid #ddd;
padding-top: 10px;
margin-top: 10px;
}
a {
color: #555;
display: block;
text-decoration: none;
padding: 6px 15px;
font-size: 13px;
line-height: 20px;
text-shadow: 0 1px 2px #FFF;
padding-left: 20px;
&:hover {
text-decoration: none;
color: #333;
background: #DDD;
}
&:active, &:focus {
text-decoration: none;
}
i {
width: 20px;
color: #888;
margin-right: 23px;
}
}
}
.sidebar-subnav {
margin-left: 0px;
padding-left: 0px;
li {
list-style: none;
}
}
@mixin expanded-sidebar {
.page-with-sidebar {
padding-left: 250px;
}
.sidebar-wrapper {
width: 250px;
.nav-sidebar {
margin-top: 20px;
position: fixed;
top: 45px;
width: 250px;
}
}
.content-wrapper {
padding: 20px;
}
}
@mixin folded-sidebar {
.page-with-sidebar {
padding-left: 50px;
}
.sidebar-wrapper {
width: 52px;
overflow-x: hidden;
.nav-sidebar {
margin-top: 20px;
position: absolute;
top: 45px;
width: 52px;
li a {
padding-left: 18px;
font-size: 14px;
padding: 10px 15px;
text-align: center;
& > span {
display: none;
}
}
}
}
}
@media (max-width: $screen-md-max) {
@include folded-sidebar;
}
@media(min-width: $screen-md-max) {
@include expanded-sidebar;
}
/**
* Note Form
*/
.comment-btn {
@extend .btn-create;
}
.reply-btn {
@extend .btn-primary;
}
.diff-file .diff-content {
tr.line_holder:hover {
&> td.line_content {
background: $hover !important;
border-color: darken($hover, 10%) !important;
}
&> td.new_line,
&> td.old_line {
background: darken($hover, 4%) !important;
border-color: darken($hover, 10%) !important;
}
}
tr.line_holder:hover > td .line_note_link {
opacity: 1.0;
filter: alpha(opacity=100);
}
}
.diff-file,
.discussion {
.new_note {
margin: 0;
border: none;
}
}
.new_note {
display: none;
}
.new_note, .edit_note {
.buttons {
float: left;
margin-top: 8px;
}
.clearfix {
margin-bottom: 0;
}
.note-preview-holder {
> p {
overflow-x: auto;
}
}
.note_text {
width: 100%;
}
}
/* loading indicator */
.notes-busy {
margin: 18px;
}
.note-image-attach {
@extend .col-md-4;
@extend .thumbnail;
margin-left: 45px;
float: none;
}
.common-note-form {
margin: 0;
background: #F9F9F9;
padding: 5px;
border: 1px solid #DDD;
}
.note-form-actions {
background: #F9F9F9;
height: 45px;
.note-form-option {
margin-top: 8px;
margin-left: 30px;
@extend .pull-left;
}
.js-notify-commit-author {
float: left;
}
.write-preview-btn {
// makes the "absolute" position for links relative to this
position: relative;
// preview/edit buttons
> a {
position: absolute;
right: 5px;
top: 8px;
}
}
}
.note-edit-form {
display: none;
font-size: 13px;
.form-actions {
padding-left: 20px;
.btn-save {
float: left;
}
.note-form-option {
float: left;
padding: 2px 0 0 25px;
}
}
}
.js-note-attachment-delete {
display: none;
}
.parallel-comment {
padding: 6px;
}
.error-alert > .alert {
margin-top: 5px;
margin-bottom: 5px;
}
.discussion-body,
.diff-file {
.notes .note {
border-color: #ddd;
padding: 10px 15px;
}
.discussion-reply-holder {
background: #f9f9f9;
padding: 10px 15px;
border-top: 1px solid #DDD;
}
}
.discussion-notes-count {
font-size: 16px;
}
.edit_note {
.markdown-area {
min-height: 140px;
}
.note-form-actions {
background: transparent;
}
}
.comment-hints {
color: #999;
background: #FFF;
padding: 5px;
margin-top: -7px;
border: 1px solid #DDD;
font-size: 13px;
}
...@@ -62,6 +62,7 @@ ul.notes { ...@@ -62,6 +62,7 @@ ul.notes {
} }
.note-body { .note-body {
@include md-typography; @include md-typography;
overflow: auto;
} }
.note-header { .note-header {
padding-bottom: 3px; padding-bottom: 3px;
...@@ -155,19 +156,26 @@ ul.notes { ...@@ -155,19 +156,26 @@ ul.notes {
} }
.add-diff-note { .add-diff-note {
background: image-url("diff_note_add.png") no-repeat left 0; margin-top: -4px;
border: none; @include border-radius(40px);
height: 22px; background: #FFF;
margin-left: -65px; padding: 4px;
font-size: 16px;
color: $link_color;
margin-left: -60px;
position: absolute; position: absolute;
width: 22px;
z-index: 10; z-index: 10;
transition: all 0.2s ease;
// "hide" it by default // "hide" it by default
opacity: 0.0; opacity: 0.0;
filter: alpha(opacity=0); filter: alpha(opacity=0);
&:hover { &:hover {
font-size: 24px;
background: $bg_primary;
color: #FFF;
@include show-add-diff-note; @include show-add-diff-note;
} }
} }
...@@ -182,169 +190,3 @@ ul.notes { ...@@ -182,169 +190,3 @@ ul.notes {
} }
} }
/**
* Note Form
*/
.comment-btn {
@extend .btn-create;
}
.reply-btn {
@extend .btn-primary;
}
.diff-file .diff-content {
tr.line_holder:hover {
&> td.line_content {
background: $hover !important;
border-color: darken($hover, 10%) !important;
}
&> td.new_line,
&> td.old_line {
background: darken($hover, 4%) !important;
border-color: darken($hover, 10%) !important;
}
}
tr.line_holder:hover > td .line_note_link {
opacity: 1.0;
filter: alpha(opacity=100);
}
}
.diff-file,
.discussion {
.new_note {
margin: 0;
border: none;
}
}
.new_note {
display: none;
.buttons {
float: left;
margin-top: 8px;
}
.clearfix {
margin-bottom: 0;
}
.note_text {
background: #FFF;
border: 1px solid #ddd;
min-height: 100px;
padding: 5px;
font-size: 14px;
box-shadow: none;
}
.note-preview-holder {
> p {
overflow-x: auto;
}
}
.note_text {
width: 100%;
}
}
/* loading indicator */
.notes-busy {
margin: 18px;
}
.note-image-attach {
@extend .col-md-4;
@extend .thumbnail;
margin-left: 45px;
float: none;
}
.common-note-form {
margin: 0;
background: #F9F9F9;
padding: 5px;
border: 1px solid #DDD;
}
.note-form-actions {
background: #F9F9F9;
height: 45px;
.note-form-option {
margin-top: 8px;
margin-left: 30px;
@extend .pull-left;
}
.js-notify-commit-author {
float: left;
}
.write-preview-btn {
// makes the "absolute" position for links relative to this
position: relative;
// preview/edit buttons
> a {
position: absolute;
right: 5px;
top: 8px;
}
}
}
.note-edit-form {
display: none;
.note_text {
border: 1px solid #DDD;
box-shadow: none;
font-size: 14px;
height: 80px;
width: 100%;
}
.form-actions {
padding-left: 20px;
.btn-save {
float: left;
}
.note-form-option {
float: left;
padding: 2px 0 0 25px;
}
}
}
.js-note-attachment-delete {
display: none;
}
.parallel-comment {
padding: 6px;
}
.error-alert > .alert {
margin-top: 5px;
margin-bottom: 5px;
}
.discussion-body,
.diff-file {
.notes .note {
border-color: #ddd;
padding: 10px 15px;
}
.discussion-reply-holder {
background: #f9f9f9;
padding: 10px 15px;
border-top: 1px solid #DDD;
}
}
.discussion-notes-count {
font-size: 16px;
}
...@@ -111,20 +111,3 @@ ...@@ -111,20 +111,3 @@
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;
}
...@@ -308,3 +308,10 @@ ul.nav.nav-projects-tabs { ...@@ -308,3 +308,10 @@ ul.nav.nav-projects-tabs {
display: none; display: none;
} }
} }
table.table.protected-branches-list tr.no-border {
th, td {
border: 0;
}
}
...@@ -17,19 +17,6 @@ ...@@ -17,19 +17,6 @@
@include border-radius(0); @include border-radius(0);
tr { tr {
td, th {
padding: 8px 10px;
line-height: 20px;
}
th {
font-weight: normal;
font-size: 15px;
border-bottom: 1px solid #CCC !important;
}
td {
border-color: #F1F1F1 !important;
border-bottom: 1px solid;
}
&:hover { &:hover {
td { td {
background: $hover; background: $hover;
......
...@@ -37,13 +37,3 @@ ...@@ -37,13 +37,3 @@
margin: 0 8px; margin: 0 8px;
} }
.votes-holder {
float: right;
width: 250px;
@media (max-width: $screen-xs-max) {
width: 100%;
margin-top: 5px;
margin-bottom: 10px;
}
}
...@@ -9,17 +9,15 @@ ...@@ -9,17 +9,15 @@
.navbar-inner { .navbar-inner {
background: #F1F1F1; background: #F1F1F1;
border-bottom: 1px solid #DDD; border-bottom: 1px solid #DDD;
.app_logo {
background-color: #DDD;
}
.nav > li > a { .nav > li > a {
color: $style_color; color: $style_color;
} }
.separator {
background: #F9F9F9;
border-left: 1px solid #DDD;
}
} }
} }
} }
.main-nav {
background: #FFF;
}
} }
...@@ -23,9 +23,8 @@ ...@@ -23,9 +23,8 @@
background-color: #436; background-color: #436;
} }
} }
.separator { .app_logo {
background: #436; background-color: #325;
border-left: 1px solid #659;
} }
.nav > li > a { .nav > li > a {
color: #98C; color: #98C;
......
...@@ -23,9 +23,8 @@ ...@@ -23,9 +23,8 @@
background-color: #272727; background-color: #272727;
} }
} }
.separator { .app_logo {
background: #272727; background-color: #222;
border-left: 1px solid #474747;
} }
} }
} }
......
...@@ -23,9 +23,8 @@ ...@@ -23,9 +23,8 @@
background-color: #373D47; background-color: #373D47;
} }
} }
.separator { .app_logo {
background: #373D47; background-color: #24272D;
border-left: 1px solid #575D67;
} }
.nav > li > a { .nav > li > a {
color: #979DA7; color: #979DA7;
......
...@@ -23,9 +23,8 @@ ...@@ -23,9 +23,8 @@
background-color: #018865; background-color: #018865;
} }
} }
.separator { .app_logo {
background: #018865; background-color: #017855;
border-left: 1px solid #11A885;
} }
.nav > li > a { .nav > li > a {
color: #ADC; color: #ADC;
......
class Admin::ApplicationSettingsController < Admin::ApplicationController
before_filter :set_application_setting
def show
end
def update
if @application_setting.update_attributes(application_setting_params)
redirect_to admin_application_settings_path,
notice: 'Application settings saved successfully'
else
render :show
end
end
private
def set_application_setting
@application_setting = ApplicationSetting.current
end
def application_setting_params
params.require(:application_setting).permit(
:default_projects_limit,
:signup_enabled,
:signin_enabled,
:gravatar_enabled,
:sign_in_text,
:home_page_url
)
end
end
class Admin::ApplicationsController < Admin::ApplicationController
before_action :set_application, only: [:show, :edit, :update, :destroy]
def index
@applications = Doorkeeper::Application.where("owner_id IS NULL")
end
def show
end
def new
@application = Doorkeeper::Application.new
end
def edit
end
def create
@application = Doorkeeper::Application.new(application_params)
if @application.save
flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create])
redirect_to admin_application_url(@application)
else
render :new
end
end
def update
if @application.update(application_params)
redirect_to admin_application_path(@application), notice: 'Application was successfully updated.'
else
render :edit
end
end
def destroy
@application.destroy
redirect_to admin_applications_url, notice: 'Application was successfully destroyed.'
end
private
def set_application
@application = Doorkeeper::Application.where("owner_id IS NULL").find(params[:id])
end
# Only allow a trusted parameter "white list" through.
def application_params
params[:doorkeeper_application].permit(:name, :redirect_uri)
end
end
...@@ -21,7 +21,7 @@ class Admin::GroupsController < Admin::ApplicationController ...@@ -21,7 +21,7 @@ class Admin::GroupsController < Admin::ApplicationController
def create def create
@group = Group.new(group_params) @group = Group.new(group_params)
@group.path = @group.name.dup.parameterize if @group.name @group.name = @group.path.dup unless @group.name
if @group.save if @group.save
@group.add_owner(current_user) @group.add_owner(current_user)
......
class Admin::KeysController < Admin::ApplicationController
before_filter :user, only: [:show, :destroy]
def show
@key = user.keys.find(params[:id])
respond_to do |format|
format.html
format.js { render nothing: true }
end
end
def destroy
key = user.keys.find(params[:id])
respond_to do |format|
if key.destroy
format.html { redirect_to [:admin, user], notice: 'User key was successfully removed.' }
else
format.html { redirect_to [:admin, user], alert: 'Failed to remove user key.' }
end
end
end
protected
def user
@user ||= User.find_by!(username: params[:user_id])
end
def key_params
params.require(:user_id, :id)
end
end
...@@ -11,6 +11,7 @@ class Admin::UsersController < Admin::ApplicationController ...@@ -11,6 +11,7 @@ class Admin::UsersController < Admin::ApplicationController
def show def show
@personal_projects = user.personal_projects @personal_projects = user.personal_projects
@joined_projects = user.projects.joined(@user) @joined_projects = user.projects.joined(@user)
@keys = user.keys.order('id DESC')
end end
def new def new
...@@ -118,7 +119,7 @@ class Admin::UsersController < Admin::ApplicationController ...@@ -118,7 +119,7 @@ class Admin::UsersController < Admin::ApplicationController
:email, :remember_me, :bio, :name, :username, :email, :remember_me, :bio, :name, :username,
:skype, :linkedin, :twitter, :website_url, :color_scheme_id, :theme_id, :force_random_password, :skype, :linkedin, :twitter, :website_url, :color_scheme_id, :theme_id, :force_random_password,
:extern_uid, :provider, :password_expires_at, :avatar, :hide_no_ssh_key, :extern_uid, :provider, :password_expires_at, :avatar, :hide_no_ssh_key,
:projects_limit, :can_create_group, :admin :projects_limit, :can_create_group, :admin, :key_id
) )
end end
end end
require 'gon' require 'gon'
class ApplicationController < ActionController::Base class ApplicationController < ActionController::Base
include Gitlab::CurrentSettings
before_filter :authenticate_user_from_token! before_filter :authenticate_user_from_token!
before_filter :authenticate_user! before_filter :authenticate_user!
before_filter :reject_blocked! before_filter :reject_blocked!
...@@ -13,7 +15,7 @@ class ApplicationController < ActionController::Base ...@@ -13,7 +15,7 @@ class ApplicationController < ActionController::Base
protect_from_forgery with: :exception protect_from_forgery with: :exception
helper_method :abilities, :can? helper_method :abilities, :can?, :current_application_settings
rescue_from Encoding::CompatibilityError do |exception| rescue_from Encoding::CompatibilityError do |exception|
log_exception(exception) log_exception(exception)
...@@ -46,6 +48,17 @@ class ApplicationController < ActionController::Base ...@@ -46,6 +48,17 @@ class ApplicationController < ActionController::Base
end end
end end
def authenticate_user!(*args)
# If user is not signe-in and tries to access root_path - redirect him to landing page
if current_application_settings.home_page_url.present?
if current_user.nil? && controller_name == 'dashboard' && action_name == 'show'
redirect_to current_application_settings.home_page_url and return
end
end
super(*args)
end
def log_exception(exception) def log_exception(exception)
application_trace = ActionDispatch::ExceptionWrapper.new(env, exception).application_trace application_trace = ActionDispatch::ExceptionWrapper.new(env, exception).application_trace
application_trace.map!{ |t| " #{t}\n" } application_trace.map!{ |t| " #{t}\n" }
...@@ -239,4 +252,63 @@ class ApplicationController < ActionController::Base ...@@ -239,4 +252,63 @@ class ApplicationController < ActionController::Base
redirect_to profile_path, notice: 'Please complete your profile with email address' and return redirect_to profile_path, notice: 'Please complete your profile with email address' and return
end end
end end
def set_filters_params
params[:sort] ||= 'newest'
params[:scope] = 'all' if params[:scope].blank?
params[:state] = 'opened' if params[:state].blank?
@filter_params = params.dup
if @project
@filter_params[:project_id] = @project.id
elsif @group
@filter_params[:group_id] = @group.id
else
# TODO: this filter ignore issues/mr created in public or
# internal repos where you are not a member. Enable this filter
# or improve current implementation to filter only issues you
# created or assigned or mentioned
#@filter_params[:authorized_only] = true
end
@filter_params
end
def set_filter_values(collection)
assignee_id = @filter_params[:assignee_id]
author_id = @filter_params[:author_id]
milestone_id = @filter_params[:milestone_id]
@sort = @filter_params[:sort].try(:humanize)
@assignees = User.where(id: collection.pluck(:assignee_id))
@authors = User.where(id: collection.pluck(:author_id))
@milestones = Milestone.where(id: collection.pluck(:milestone_id))
if assignee_id.present? && !assignee_id.to_i.zero?
@assignee = @assignees.find_by(id: assignee_id)
end
if author_id.present? && !author_id.to_i.zero?
@author = @authors.find_by(id: author_id)
end
if milestone_id.present? && !milestone_id.to_i.zero?
@milestone = @milestones.find_by(id: milestone_id)
end
end
def get_issues_collection
set_filters_params
issues = IssuesFinder.new.execute(current_user, @filter_params)
set_filter_values(issues)
issues
end
def get_merge_requests_collection
set_filters_params
merge_requests = MergeRequestsFinder.new.execute(current_user, @filter_params)
set_filter_values(merge_requests)
merge_requests
end
end end
...@@ -3,8 +3,6 @@ class DashboardController < ApplicationController ...@@ -3,8 +3,6 @@ class DashboardController < ApplicationController
before_filter :load_projects, except: [:projects] before_filter :load_projects, except: [:projects]
before_filter :event_filter, only: :show before_filter :event_filter, only: :show
before_filter :default_filter, only: [:issues, :merge_requests]
def show def show
# Fetch only 30 projects. # Fetch only 30 projects.
...@@ -55,13 +53,13 @@ class DashboardController < ApplicationController ...@@ -55,13 +53,13 @@ class DashboardController < ApplicationController
end end
def merge_requests def merge_requests
@merge_requests = MergeRequestsFinder.new.execute(current_user, params) @merge_requests = get_merge_requests_collection
@merge_requests = @merge_requests.page(params[:page]).per(20) @merge_requests = @merge_requests.page(params[:page]).per(20)
@merge_requests = @merge_requests.preload(:author, :target_project) @merge_requests = @merge_requests.preload(:author, :target_project)
end end
def issues def issues
@issues = IssuesFinder.new.execute(current_user, params) @issues = get_issues_collection
@issues = @issues.page(params[:page]).per(20) @issues = @issues.page(params[:page]).per(20)
@issues = @issues.preload(:author, :project) @issues = @issues.preload(:author, :project)
...@@ -76,10 +74,4 @@ class DashboardController < ApplicationController ...@@ -76,10 +74,4 @@ class DashboardController < ApplicationController
def load_projects def load_projects
@projects = current_user.authorized_projects.sorted_by_activity.non_archived @projects = current_user.authorized_projects.sorted_by_activity.non_archived
end end
def default_filter
params[:scope] = 'assigned-to-me' if params[:scope].blank?
params[:state] = 'opened' if params[:state].blank?
params[:authorized_only] = true
end
end end
class GithubImportsController < ApplicationController
before_filter :github_auth, except: :callback
rescue_from Octokit::Unauthorized, with: :github_unauthorized
def callback
token = client.auth_code.get_token(params[:code]).token
current_user.github_access_token = token
current_user.save
redirect_to status_github_import_url
end
def status
@repos = octo_client.repos
octo_client.orgs.each do |org|
@repos += octo_client.repos(org.login)
end
@already_added_projects = current_user.created_projects.where(import_type: "github")
already_added_projects_names = @already_added_projects.pluck(:import_source)
@repos.reject!{|repo| already_added_projects_names.include? repo.full_name}
end
def create
@repo_id = params[:repo_id].to_i
repo = octo_client.repo(@repo_id)
target_namespace = params[:new_namespace].presence || repo.owner.login
existing_namespace = Namespace.find_by("path = ? OR name = ?", target_namespace, target_namespace)
if existing_namespace
if existing_namespace.owner == current_user
namespace = existing_namespace
else
@already_been_taken = true
@target_namespace = target_namespace
@project_name = repo.name
render and return
end
else
namespace = Group.create(name: target_namespace, path: target_namespace, owner: current_user)
namespace.add_owner(current_user)
end
Gitlab::Github::ProjectCreator.new(repo, namespace, current_user).execute
end
private
def client
@client ||= Gitlab::Github::Client.new.client
end
def octo_client
Octokit.auto_paginate = true
@octo_client ||= Octokit::Client.new(:access_token => current_user.github_access_token)
end
def github_auth
if current_user.github_access_token.blank?
go_to_gihub_for_permissions
end
end
def go_to_gihub_for_permissions
redirect_to client.auth_code.authorize_url({
redirect_uri: callback_github_import_url,
scope: "repo, user, user:email"
})
end
def github_unauthorized
go_to_gihub_for_permissions
end
end
...@@ -11,8 +11,6 @@ class GroupsController < ApplicationController ...@@ -11,8 +11,6 @@ class GroupsController < ApplicationController
# Load group projects # Load group projects
before_filter :load_projects, except: [:new, :create, :projects, :edit, :update] before_filter :load_projects, except: [:new, :create, :projects, :edit, :update]
before_filter :default_filter, only: [:issues, :merge_requests]
layout :determine_layout layout :determine_layout
before_filter :set_title, only: [:new, :create] before_filter :set_title, only: [:new, :create]
...@@ -23,7 +21,7 @@ class GroupsController < ApplicationController ...@@ -23,7 +21,7 @@ class GroupsController < ApplicationController
def create def create
@group = Group.new(group_params) @group = Group.new(group_params)
@group.path = @group.name.dup.parameterize if @group.name @group.name = @group.path.dup unless @group.name
if @group.save if @group.save
@group.add_owner(current_user) @group.add_owner(current_user)
...@@ -47,13 +45,13 @@ class GroupsController < ApplicationController ...@@ -47,13 +45,13 @@ class GroupsController < ApplicationController
end end
def merge_requests def merge_requests
@merge_requests = MergeRequestsFinder.new.execute(current_user, params) @merge_requests = get_merge_requests_collection
@merge_requests = @merge_requests.page(params[:page]).per(20) @merge_requests = @merge_requests.page(params[:page]).per(20)
@merge_requests = @merge_requests.preload(:author, :target_project) @merge_requests = @merge_requests.preload(:author, :target_project)
end end
def issues def issues
@issues = IssuesFinder.new.execute(current_user, params) @issues = get_issues_collection
@issues = @issues.page(params[:page]).per(20) @issues = @issues.page(params[:page]).per(20)
@issues = @issues.preload(:author, :project) @issues = @issues.preload(:author, :project)
...@@ -148,18 +146,6 @@ class GroupsController < ApplicationController ...@@ -148,18 +146,6 @@ class GroupsController < ApplicationController
end end
end end
def default_filter
if params[:scope].blank?
if current_user
params[:scope] = 'assigned-to-me'
else
params[:scope] = 'all'
end
end
params[:state] = 'opened' if params[:state].blank?
params[:group_id] = @group.id
end
def group_params def group_params
params.require(:group).permit(:name, :description, :path, :avatar) params.require(:group).permit(:name, :description, :path, :avatar)
end end
......
class Oauth::ApplicationsController < Doorkeeper::ApplicationsController
before_filter :authenticate_user!
layout "profile"
def index
head :forbidden and return
end
def create
@application = Doorkeeper::Application.new(application_params)
@application.owner = current_user
if @application.save
flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :create])
redirect_to oauth_application_url(@application)
else
render :new
end
end
def destroy
if @application.destroy
flash[:notice] = I18n.t(:notice, scope: [:doorkeeper, :flash, :applications, :destroy])
end
redirect_to applications_profile_url
end
private
def set_application
@application = current_user.oauth_applications.find(params[:id])
end
rescue_from ActiveRecord::RecordNotFound do |exception|
render "errors/not_found", layout: "errors", status: 404
end
end
class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
before_filter :authenticate_resource_owner!
layout "profile"
def new
if pre_auth.authorizable?
if skip_authorization? || matching_token?
auth = authorization.authorize
redirect_to auth.redirect_uri
else
render "doorkeeper/authorizations/new"
end
else
render "doorkeeper/authorizations/error"
end
end
# TODO: Handle raise invalid authorization
def create
redirect_or_render authorization.authorize
end
def destroy
redirect_or_render authorization.deny
end
private
def matching_token?
Doorkeeper::AccessToken.matching_token_for(pre_auth.client,
current_resource_owner.id,
pre_auth.scopes)
end
def redirect_or_render(auth)
if auth.redirectable?
redirect_to auth.redirect_uri
else
render json: auth.body, status: auth.status
end
end
def pre_auth
@pre_auth ||=
Doorkeeper::OAuth::PreAuthorization.new(Doorkeeper.configuration,
server.client_via_uid,
params)
end
def authorization
@authorization ||= strategy.request
end
def strategy
@strategy ||= server.authorization_request(pre_auth.response_type)
end
end
class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicationsController
layout "profile"
def destroy
Doorkeeper::AccessToken.revoke_all_for(params[:id], current_resource_owner)
redirect_to applications_profile_url, notice: I18n.t(:notice, scope: [:doorkeeper, :flash, :authorized_applications, :destroy])
end
end
...@@ -65,7 +65,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController ...@@ -65,7 +65,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
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 rescue Gitlab::OAuth::ForbiddenAction => e
flash[:notice] = e.message flash[:notice] = e.message
redirect_to new_user_session_path redirect_to new_user_session_path
end end
......
...@@ -13,6 +13,11 @@ class ProfilesController < ApplicationController ...@@ -13,6 +13,11 @@ class ProfilesController < ApplicationController
def design def design
end end
def applications
@applications = current_user.oauth_applications
@authorized_tokens = current_user.oauth_authorized_tokens
end
def update def update
user_params.except!(:email) if @user.ldap_user? user_params.except!(:email) if @user.ldap_user?
......
...@@ -29,31 +29,4 @@ class Projects::ApplicationController < ApplicationController ...@@ -29,31 +29,4 @@ class Projects::ApplicationController < ApplicationController
redirect_to project_tree_path(@project, @ref), notice: "This action is not allowed unless you are on top of a branch" 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
...@@ -18,9 +18,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -18,9 +18,7 @@ class Projects::IssuesController < Projects::ApplicationController
def index def index
terms = params['issue_search'] terms = params['issue_search']
set_filter_variables(@project.issues) @issues = get_issues_collection
@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)
......
...@@ -17,9 +17,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -17,9 +17,7 @@ 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
set_filter_variables(@project.merge_requests) @merge_requests = get_merge_requests_collection
@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)
end end
...@@ -29,6 +27,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -29,6 +27,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
respond_to do |format| respond_to do |format|
format.html format.html
format.json { render json: @merge_request }
format.diff { render text: @merge_request.to_diff(current_user) } format.diff { render text: @merge_request.to_diff(current_user) }
format.patch { render text: @merge_request.to_patch(current_user) } format.patch { render text: @merge_request.to_patch(current_user) }
end end
...@@ -106,15 +105,15 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -106,15 +105,15 @@ class Projects::MergeRequestsController < Projects::ApplicationController
if @merge_request.unchecked? if @merge_request.unchecked?
@merge_request.check_if_can_be_merged @merge_request.check_if_can_be_merged
end end
render json: {merge_status: @merge_request.merge_status_name}
render json: { merge_status: @merge_request.merge_status_name }
end end
def automerge def automerge
return access_denied! unless allowed_to_merge? return access_denied! unless allowed_to_merge?
if @merge_request.open? && @merge_request.can_be_merged? if @merge_request.open? && @merge_request.can_be_merged?
@merge_request.should_remove_source_branch = params[:should_remove_source_branch] AutoMergeWorker.perform_async(@merge_request.id, current_user.id, params)
@merge_request.automerge!(current_user, params[:commit_message])
@status = true @status = true
else else
@status = false @status = false
......
...@@ -11,7 +11,7 @@ class Projects::MilestonesController < Projects::ApplicationController ...@@ -11,7 +11,7 @@ class Projects::MilestonesController < Projects::ApplicationController
respond_to :html respond_to :html
def index def index
@milestones = case params[:f] @milestones = case params[:state]
when 'all'; @project.milestones.order("state, due_date DESC") when 'all'; @project.milestones.order("state, due_date DESC")
when 'closed'; @project.milestones.closed.order("due_date DESC") when 'closed'; @project.milestones.closed.order("due_date DESC")
else @project.milestones.active.order("due_date ASC") else @project.milestones.active.order("due_date ASC")
......
...@@ -15,6 +15,24 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController ...@@ -15,6 +15,24 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController
redirect_to project_protected_branches_path(@project) redirect_to project_protected_branches_path(@project)
end end
def update
protected_branch = @project.protected_branches.find(params[:id])
if protected_branch &&
protected_branch.update_attributes(
developers_can_push: params[:developers_can_push]
)
respond_to do |format|
format.json { render :json => protected_branch, status: :ok }
end
else
respond_to do |format|
format.json { render json: protected_branch.errors, status: :unprocessable_entity }
end
end
end
def destroy def destroy
@project.protected_branches.find(params[:id]).destroy @project.protected_branches.find(params[:id]).destroy
...@@ -27,6 +45,6 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController ...@@ -27,6 +45,6 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController
private private
def protected_branch_params def protected_branch_params
params.require(:protected_branch).permit(:name) params.require(:protected_branch).permit(:name, :developers_can_push)
end end
end end
...@@ -24,8 +24,7 @@ class Projects::ServicesController < Projects::ApplicationController ...@@ -24,8 +24,7 @@ class Projects::ServicesController < Projects::ApplicationController
end end
def test def test
data = GitPushService.new.sample_data(project, current_user) data = Gitlab::PushDataBuilder.build_sample(project, current_user)
@service.execute(data) @service.execute(data)
redirect_to :back redirect_to :back
...@@ -42,7 +41,7 @@ class Projects::ServicesController < Projects::ApplicationController ...@@ -42,7 +41,7 @@ class Projects::ServicesController < Projects::ApplicationController
: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, :bamboo_url, :username, :password, :user_key, :device, :priority, :sound, :bamboo_url, :username, :password,
:build_key, :server :build_key, :server, :teamcity_url, :build_type
) )
end end
end end
...@@ -13,6 +13,7 @@ class Projects::TagsController < Projects::ApplicationController ...@@ -13,6 +13,7 @@ class Projects::TagsController < Projects::ApplicationController
def create def create
result = CreateTagService.new(@project, current_user). result = CreateTagService.new(@project, current_user).
execute(params[:tag_name], params[:ref], params[:message]) execute(params[:tag_name], params[:ref], params[:message])
if result[:status] == :success if result[:status] == :success
@tag = result[:tag] @tag = result[:tag]
redirect_to project_tags_path(@project) redirect_to project_tags_path(@project)
......
...@@ -44,6 +44,9 @@ class ProjectsController < ApplicationController ...@@ -44,6 +44,9 @@ class ProjectsController < ApplicationController
def transfer def transfer
::Projects::TransferService.new(project, current_user, project_params).execute ::Projects::TransferService.new(project, current_user, project_params).execute
if @project.errors[:namespace_id].present?
flash[:alert] = @project.errors[:namespace_id].first
end
end end
def show def show
...@@ -98,11 +101,20 @@ class ProjectsController < ApplicationController ...@@ -98,11 +101,20 @@ class ProjectsController < ApplicationController
def autocomplete_sources def autocomplete_sources
note_type = params['type'] note_type = params['type']
note_id = params['type_id'] note_id = params['type_id']
autocomplete = ::Projects::AutocompleteService.new(@project)
participants = ::Projects::ParticipantsService.new(@project).execute(note_type, note_id) participants = ::Projects::ParticipantsService.new(@project).execute(note_type, note_id)
emojis = Emoji.names.map do |e|
{
name: e,
path: view_context.image_url("emoji/#{e}.png")
}
end
@suggestions = { @suggestions = {
emojis: Emoji.names.map { |e| { name: e, path: view_context.image_url("emoji/#{e}.png") } }, emojis: emojis,
issues: @project.issues.select([:iid, :title, :description]), issues: autocomplete.issues,
mergerequests: @project.merge_requests.select([:iid, :title, :description]), mergerequests: autocomplete.merge_requests,
members: participants members: participants
} }
......
...@@ -26,7 +26,9 @@ class RegistrationsController < Devise::RegistrationsController ...@@ -26,7 +26,9 @@ class RegistrationsController < Devise::RegistrationsController
private private
def signup_enabled? def signup_enabled?
redirect_to new_user_session_path unless Gitlab.config.gitlab.signup_enabled unless current_application_settings.signup_enabled?
redirect_to(new_user_session_path)
end
end end
def sign_up_params def sign_up_params
......
class SessionsController < Devise::SessionsController class SessionsController < Devise::SessionsController
def new def new
redirect_path = if request.referer.present? && (params['redirect_to_referer'] == 'yes') redirect_path =
referer_uri = URI(request.referer) if request.referer.present? && (params['redirect_to_referer'] == 'yes')
if referer_uri.host == Gitlab.config.gitlab.host referer_uri = URI(request.referer)
referer_uri.path if referer_uri.host == Gitlab.config.gitlab.host
else referer_uri.path
request.fullpath else
end request.fullpath
else end
request.fullpath else
end request.fullpath
end
# Prevent a 'you are already signed in' message directly after signing: # Prevent a 'you are already signed in' message directly after signing:
# we should never redirect to '/users/sign_in' after signing in successfully. # we should never redirect to '/users/sign_in' after signing in successfully.
......
...@@ -114,6 +114,10 @@ module ApplicationHelper ...@@ -114,6 +114,10 @@ module ApplicationHelper
Gitlab::Theme.css_class_by_id(current_user.try(:theme_id)) Gitlab::Theme.css_class_by_id(current_user.try(:theme_id))
end end
def theme_type
Gitlab::Theme.type_css_class_by_id(current_user.try(:theme_id))
end
def user_color_scheme_class def user_color_scheme_class
COLOR_SCHEMES[current_user.try(:color_scheme_id)] if defined?(current_user) COLOR_SCHEMES[current_user.try(:color_scheme_id)] if defined?(current_user)
end end
...@@ -271,4 +275,34 @@ module ApplicationHelper ...@@ -271,4 +275,34 @@ module ApplicationHelper
def promo_url def promo_url
'https://' + promo_host 'https://' + promo_host
end end
def page_filter_path(options={})
exist_opts = {
state: params[:state],
scope: params[:scope],
label_name: params[:label_name],
milestone_id: params[:milestone_id],
assignee_id: params[:assignee_id],
author_id: params[:author_id],
sort: params[:sort],
}
options = exist_opts.merge(options)
path = request.path
path << "?#{options.to_param}"
path
end
def outdated_browser?
browser.ie? && browser.version.to_i < 10
end
def path_to_key(key, admin = false)
if admin
admin_user_key_path(@user, key)
else
profile_key_path(key)
end
end
end end
module ApplicationSettingsHelper
def gravatar_enabled?
current_application_settings.gravatar_enabled?
end
def signup_enabled?
current_application_settings.signup_enabled?
end
def signin_enabled?
current_application_settings.signin_enabled?
end
def extra_sign_in_text
current_application_settings.sign_in_text
end
end
module DashboardHelper module DashboardHelper
def filter_path(entity, options={})
exist_opts = {
state: params[:state],
scope: params[:scope],
project_id: params[:project_id],
}
options = exist_opts.merge(options)
path = request.path
path << "?#{options.to_param}"
path
end
def entities_per_project(project, entity)
case entity.to_sym
when :issue then @issues.where(project_id: project.id)
when :merge_request then @merge_requests.where(target_project_id: project.id)
else
[]
end.count
end
def projects_dashboard_filter_path(options={}) def projects_dashboard_filter_path(options={})
exist_opts = { exist_opts = {
sort: params[:sort], sort: params[:sort],
scope: params[:scope], scope: params[:scope],
group: params[:group], group: params[:group],
tag: params[:tag],
visibility_level: params[:visibility_level],
} }
options = exist_opts.merge(options) options = exist_opts.merge(options)
...@@ -36,32 +15,11 @@ module DashboardHelper ...@@ -36,32 +15,11 @@ module DashboardHelper
path path
end end
def assigned_entities_count(current_user, entity, scope = nil) def assigned_issues_dashboard_path
items = current_user.send('assigned_' + entity.pluralize) issues_dashboard_path(assignee_id: current_user.id)
get_count(items, scope)
end end
def authored_entities_count(current_user, entity, scope = nil) def assigned_mrs_dashboard_path
items = current_user.send(entity.pluralize) merge_requests_dashboard_path(assignee_id: current_user.id)
get_count(items, scope)
end
def authorized_entities_count(current_user, entity, scope = nil)
items = entity.classify.constantize
get_count(items, scope, true, current_user)
end
protected
def get_count(items, scope, get_authorized = false, current_user = nil)
items = items.opened
if scope.kind_of?(Group)
items = items.of_group(scope)
elsif scope.kind_of?(Project)
items = items.of_projects(scope)
elsif get_authorized
items = items.of_projects(current_user.authorized_projects)
end
items.count
end end
end end
...@@ -117,4 +117,22 @@ module DiffHelper ...@@ -117,4 +117,22 @@ module DiffHelper
[comments_left, comments_right] [comments_left, comments_right]
end end
def inline_diff_btn
params_copy = params.dup
params_copy[:view] = 'inline'
link_to url_for(params_copy), id: "commit-diff-viewtype", class: (params[:view] != 'parallel' ? 'btn active' : 'btn') do
'Inline'
end
end
def parallel_diff_btn
params_copy = params.dup
params_copy[:view] = 'parallel'
link_to url_for(params_copy), id: "commit-diff-viewtype", class: (params[:view] == 'parallel' ? 'btn active' : 'btn') do
'Side-by-side'
end
end
end end
...@@ -21,15 +21,14 @@ module EventsHelper ...@@ -21,15 +21,14 @@ module EventsHelper
def event_filter_link(key, tooltip) def event_filter_link(key, tooltip)
key = key.to_s key = key.to_s
inactive = if @event_filter.active? key active = if @event_filter.active? key
nil 'active'
else end
'inactive'
end
content_tag :div, class: "filter_icon #{inactive}" do content_tag :li, class: "filter_icon #{active}" do
link_to request.path, class: 'has_tooltip event_filter_link', id: "#{key}_event_filter", 'data-original-title' => tooltip do link_to request.path, class: 'has_tooltip event_filter_link', id: "#{key}_event_filter", 'data-original-title' => tooltip do
content_tag :i, nil, class: icon_for_event[key] content_tag(:i, nil, class: icon_for_event[key]) +
content_tag(:span, ' ' + tooltip)
end end
end end
end end
......
...@@ -6,7 +6,7 @@ module GroupsHelper ...@@ -6,7 +6,7 @@ module GroupsHelper
def leave_group_message(group) def leave_group_message(group)
"Are you sure you want to leave \"#{group}\" group?" "Are you sure you want to leave \"#{group}\" group?"
end end
def should_user_see_group_roles?(user, group) def should_user_see_group_roles?(user, group)
if user if user
user.is_admin? || group.members.exists?(user_id: user.id) user.is_admin? || group.members.exists?(user_id: user.id)
...@@ -33,15 +33,11 @@ module GroupsHelper ...@@ -33,15 +33,11 @@ module GroupsHelper
title title
end end
def group_filter_path(entity, options={}) def group_settings_page?
exist_opts = { if current_controller?('groups')
status: params[:status] current_action?('edit') || current_action?('projects')
} else
false
options = exist_opts.merge(options) end
path = request.path
path << "?#{options.to_param}"
path
end end
end end
module MilestonesHelper
def milestones_filter_path(opts = {})
if @project
project_milestones_path(@project, opts)
elsif @group
group_milestones_path(@group, opts)
end
end
end
...@@ -52,8 +52,11 @@ module NotesHelper ...@@ -52,8 +52,11 @@ module NotesHelper
discussion_id: discussion_id discussion_id: discussion_id
} }
button_tag '', class: 'btn add-diff-note js-add-diff-note-button', button_tag(class: 'btn add-diff-note js-add-diff-note-button',
data: data, title: 'Add a comment to this line' data: data,
title: 'Add a comment to this line') do
content_tag :i, nil, class: 'fa fa-comment-o'
end
end end
def link_to_reply_diff(note) def link_to_reply_diff(note)
......
...@@ -14,6 +14,6 @@ module ProfileHelper ...@@ -14,6 +14,6 @@ module ProfileHelper
end end
def show_profile_remove_tab? def show_profile_remove_tab?
gitlab_config.signup_enabled signup_enabled?
end end
end end
...@@ -68,48 +68,6 @@ module ProjectsHelper ...@@ -68,48 +68,6 @@ module ProjectsHelper
project_nav_tabs.include? name project_nav_tabs.include? name
end end
def selected_label?(label_name)
params[:label_name].to_s.split(',').include?(label_name)
end
def labels_filter_path(label_name)
label_name =
if selected_label?(label_name)
params[:label_name].split(',').reject { |l| l == label_name }.join(',')
elsif params[:label_name].present?
"#{params[:label_name]},#{label_name}"
else
label_name
end
project_filter_path(label_name: label_name)
end
def label_filter_class(label_name)
if selected_label?(label_name)
'label-filter-item active'
else
'label-filter-item light'
end
end
def project_filter_path(options={})
exist_opts = {
state: params[:state],
scope: params[:scope],
label_name: params[:label_name],
milestone_id: params[:milestone_id],
assignee_id: params[:assignee_id],
sort: params[:sort],
}
options = exist_opts.merge(options)
path = request.path
path << "?#{options.to_param}"
path
end
def project_active_milestones def project_active_milestones
@project.milestones.active.order("due_date, title ASC") @project.milestones.active.order("due_date, title ASC")
end end
...@@ -279,4 +237,25 @@ module ProjectsHelper ...@@ -279,4 +237,25 @@ module ProjectsHelper
result.password = '*****' if result.password.present? result.password = '*****' if result.password.present?
result result
end end
def project_wiki_path_with_version(proj, page, version, is_newest)
url_params = is_newest ? {} : { version_id: version }
project_wiki_path(proj, page, url_params)
end
def project_status_css_class(status)
case status
when "started"
"active"
when "failed"
"danger"
when "finished"
"success"
end
end
def github_import_enabled?
Gitlab.config.omniauth.enabled && enabled_oauth_providers.include?(:github)
end
end end
...@@ -17,4 +17,13 @@ module SelectsHelper ...@@ -17,4 +17,13 @@ module SelectsHelper
project_id = opts[:project_id] || @project.id project_id = opts[:project_id] || @project.id
hidden_field_tag(id, value, class: css_class, 'data-placeholder' => placeholder, 'data-project-id' => project_id) hidden_field_tag(id, value, class: css_class, 'data-placeholder' => placeholder, 'data-project-id' => project_id)
end end
def groups_select_tag(id, opts = {})
css_class = "ajax-groups-select "
css_class << "multiselect " if opts[:multiple]
css_class << (opts[:class] || '')
value = opts[:selected] || ''
hidden_field_tag(id, value, class: css_class)
end
end end
...@@ -28,6 +28,10 @@ module TabHelper ...@@ -28,6 +28,10 @@ module TabHelper
# nav_link(controller: [:tree, :refs]) { "Hello" } # nav_link(controller: [:tree, :refs]) { "Hello" }
# # => '<li class="active">Hello</li>' # # => '<li class="active">Hello</li>'
# #
# # Several paths
# nav_link(path: ['tree#show', 'profile#show']) { "Hello" }
# # => '<li class="active">Hello</li>'
#
# # Shorthand path # # Shorthand path
# nav_link(path: 'tree#show') { "Hello" } # nav_link(path: 'tree#show') { "Hello" }
# # => '<li class="active">Hello</li>' # # => '<li class="active">Hello</li>'
...@@ -38,25 +42,7 @@ module TabHelper ...@@ -38,25 +42,7 @@ module TabHelper
# #
# Returns a list item element String # Returns a list item element String
def nav_link(options = {}, &block) def nav_link(options = {}, &block)
if path = options.delete(:path) klass = active_nav_link?(options) ? 'active' : ''
if path.respond_to?(:each)
c = path.map { |p| p.split('#').first }
a = path.map { |p| p.split('#').last }
else
c, a, _ = path.split('#')
end
else
c = options.delete(:controller)
a = options.delete(:action)
end
if c && a
# When given both options, make sure BOTH are active
klass = current_controller?(*c) && current_action?(*a) ? 'active' : ''
else
# Otherwise check EITHER option
klass = current_controller?(*c) || current_action?(*a) ? 'active' : ''
end
# Add our custom class into the html_options, which may or may not exist # Add our custom class into the html_options, which may or may not exist
# and which may or may not already have a :class key # and which may or may not already have a :class key
...@@ -72,6 +58,34 @@ module TabHelper ...@@ -72,6 +58,34 @@ module TabHelper
end end
end end
def active_nav_link?(options)
if path = options.delete(:path)
unless path.respond_to?(:each)
path = [path]
end
path.any? do |single_path|
current_path?(single_path)
end
else
c = options.delete(:controller)
a = options.delete(:action)
if c && a
# When given both options, make sure BOTH are true
current_controller?(*c) && current_action?(*a)
else
# Otherwise check EITHER option
current_controller?(*c) || current_action?(*a)
end
end
end
def current_path?(path)
c, a, _ = path.split('#')
current_controller?(c) && current_action?(a)
end
def project_tab_class def project_tab_class
return "active" if current_page?(controller: "/projects", action: :edit, id: @project) return "active" if current_page?(controller: "/projects", action: :edit, id: @project)
......
...@@ -53,13 +53,41 @@ module TreeHelper ...@@ -53,13 +53,41 @@ module TreeHelper
File.join(*args) File.join(*args)
end end
def allowed_tree_edit? def allowed_tree_edit?(project = nil, ref = nil)
return false unless @repository.branch_names.include?(@ref) project ||= @project
ref ||= @ref
return false unless project.repository.branch_names.include?(ref)
if @project.protected_branch? @ref if project.protected_branch? ref
can?(current_user, :push_code_to_protected_branches, @project) can?(current_user, :push_code_to_protected_branches, project)
else else
can?(current_user, :push_code, @project) can?(current_user, :push_code, project)
end
end
def edit_blob_link(project, ref, path, options = {})
blob =
begin
project.repository.blob_at(ref, path)
rescue
nil
end
if blob && blob.text?
text = 'Edit'
after = options[:after] || ''
from_mr = options[:from_merge_request_id]
link_opts = {}
link_opts[:from_merge_request_id] = from_mr if from_mr
cls = 'btn btn-small'
if allowed_tree_edit?(project, ref)
link_to text, project_edit_tree_path(project, tree_join(ref, path),
link_opts), class: cls
else
content_tag :span, text, class: cls + ' disabled'
end + after.html_safe
else
''
end end
end end
...@@ -85,6 +113,16 @@ module TreeHelper ...@@ -85,6 +113,16 @@ module TreeHelper
tree_join(@ref, file) tree_join(@ref, file)
end end
# returns the relative path of the first subdir that doesn't have only one directory descendand
def flatten_tree(tree)
subtree = Gitlab::Git::Tree.where(@repository, @commit.id, tree.path)
if subtree.count == 1 && subtree.first.dir?
return tree_join(tree.name, flatten_tree(subtree.first))
else
return tree.name
end
end
def leave_edit_message def leave_edit_message
"Leave edit mode?\nAll unsaved changes will be lost." "Leave edit mode?\nAll unsaved changes will be lost."
end end
......
class ApplicationSetting < ActiveRecord::Base
validates :home_page_url, allow_blank: true,
format: { with: URI::regexp(%w(http https)), message: "should be a valid url" },
if: :home_page_url_column_exist
def self.current
ApplicationSetting.last
end
def self.create_from_defaults
create(
default_projects_limit: Settings.gitlab['default_projects_limit'],
signup_enabled: Settings.gitlab['signup_enabled'],
signin_enabled: Settings.gitlab['signin_enabled'],
gravatar_enabled: Settings.gravatar['enabled'],
sign_in_text: Settings.extra['sign_in_text'],
)
end
def home_page_url_column_exist
ActiveRecord::Base.connection.column_exists?(:application_settings, :home_page_url)
end
end
...@@ -174,7 +174,7 @@ class Event < ActiveRecord::Base ...@@ -174,7 +174,7 @@ class Event < ActiveRecord::Base
def valid_push? def valid_push?
data[:ref] && ref_name.present? data[:ref] && ref_name.present?
rescue => ex rescue
false false
end end
......
...@@ -21,7 +21,7 @@ class Group < Namespace ...@@ -21,7 +21,7 @@ class Group < Namespace
has_many :users, through: :group_members has_many :users, through: :group_members
validate :avatar_type, if: ->(user) { user.avatar_changed? } validate :avatar_type, if: ->(user) { user.avatar_changed? }
validates :avatar, file_size: { maximum: 100.kilobytes.to_i } validates :avatar, file_size: { maximum: 200.kilobytes.to_i }
mount_uploader :avatar, AttachmentUploader mount_uploader :avatar, AttachmentUploader
......
...@@ -48,7 +48,7 @@ class WebHook < ActiveRecord::Base ...@@ -48,7 +48,7 @@ class WebHook < ActiveRecord::Base
verify: false, verify: false,
basic_auth: auth) basic_auth: auth)
end end
rescue SocketError, Errno::ECONNREFUSED => e rescue SocketError, Errno::ECONNREFUSED, Net::OpenTimeout => e
logger.error("WebHook Error => #{e}") logger.error("WebHook Error => #{e}")
false false
end end
......
...@@ -189,7 +189,9 @@ class MergeRequest < ActiveRecord::Base ...@@ -189,7 +189,9 @@ class MergeRequest < ActiveRecord::Base
end end
def automerge!(current_user, commit_message = nil) def automerge!(current_user, commit_message = nil)
MergeRequests::AutoMergeService.new.execute(self, current_user, commit_message) MergeRequests::AutoMergeService.
new(target_project, current_user).
execute(self, commit_message)
end end
def open? def open?
......
...@@ -296,6 +296,7 @@ class Note < ActiveRecord::Base ...@@ -296,6 +296,7 @@ class Note < ActiveRecord::Base
# If not - its outdated diff # If not - its outdated diff
def active? def active?
return true unless self.diff return true unless self.diff
return false unless noteable
noteable.diffs.each do |mr_diff| noteable.diffs.each do |mr_diff|
next unless mr_diff.new_path == self.diff.new_path next unless mr_diff.new_path == self.diff.new_path
......
...@@ -6,12 +6,13 @@ class Notification ...@@ -6,12 +6,13 @@ class Notification
N_PARTICIPATING = 1 N_PARTICIPATING = 1
N_WATCH = 2 N_WATCH = 2
N_GLOBAL = 3 N_GLOBAL = 3
N_MENTION = 4
attr_accessor :target attr_accessor :target
class << self class << self
def notification_levels def notification_levels
[N_DISABLED, N_PARTICIPATING, N_WATCH] [N_DISABLED, N_PARTICIPATING, N_WATCH, N_MENTION]
end end
def options_with_labels def options_with_labels
...@@ -19,12 +20,13 @@ class Notification ...@@ -19,12 +20,13 @@ class Notification
disabled: N_DISABLED, disabled: N_DISABLED,
participating: N_PARTICIPATING, participating: N_PARTICIPATING,
watch: N_WATCH, watch: N_WATCH,
mention: N_MENTION,
global: N_GLOBAL global: N_GLOBAL
} }
end end
def project_notification_levels def project_notification_levels
[N_DISABLED, N_PARTICIPATING, N_WATCH, N_GLOBAL] [N_DISABLED, N_PARTICIPATING, N_WATCH, N_GLOBAL, N_MENTION]
end end
end end
...@@ -48,6 +50,10 @@ class Notification ...@@ -48,6 +50,10 @@ class Notification
target.notification_level == N_GLOBAL target.notification_level == N_GLOBAL
end end
def mention?
target.notification_level == N_MENTION
end
def level def level
target.notification_level target.notification_level
end end
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
File mode changed from 100755 to 100644
This diff is collapsed.
File mode changed from 100755 to 100644
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
File mode changed from 100755 to 100644
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