mirror of
https://github.com/wahyd4/gitlabhq.git
synced 2026-08-09 04:36:11 +10:00
Merge branch 'master' into gitlab-flow
Conflicts: CONTRIBUTING.md
This commit is contained in:
@@ -38,3 +38,4 @@ public/uploads.*
|
||||
public/assets/
|
||||
.envrc
|
||||
dump.rdb
|
||||
tags
|
||||
|
||||
@@ -5,6 +5,8 @@ targets:
|
||||
debian-7: &wheezy
|
||||
build_dependencies:
|
||||
- libicu-dev
|
||||
- cmake
|
||||
- pkg-config
|
||||
dependencies:
|
||||
- libicu48
|
||||
- libpcre3
|
||||
@@ -13,6 +15,8 @@ targets:
|
||||
ubuntu-14.04:
|
||||
build_dependencies:
|
||||
- libicu-dev
|
||||
- cmake
|
||||
- pkg-config
|
||||
dependencies:
|
||||
- libicu52
|
||||
- libpcre3
|
||||
@@ -20,6 +24,8 @@ targets:
|
||||
centos-6:
|
||||
build_dependencies:
|
||||
- libicu-devel
|
||||
- cmake
|
||||
- pkgconfig
|
||||
dependencies:
|
||||
- libicu
|
||||
- pcre
|
||||
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
language: ruby
|
||||
env:
|
||||
global:
|
||||
- TRAVIS=true
|
||||
matrix:
|
||||
- TASK=spinach_project DB=mysql
|
||||
- TASK=spinach_other DB=mysql
|
||||
- TASK=spec:api DB=mysql
|
||||
- TASK=spec:feature DB=mysql
|
||||
- TASK=spec:other DB=mysql
|
||||
- TASK=jasmine:ci DB=mysql
|
||||
- TASK=spinach_project DB=postgresql
|
||||
- TASK=spinach_other DB=postgresql
|
||||
- TASK=spec:api DB=postgresql
|
||||
- TASK=spec:feature DB=postgresql
|
||||
- TASK=spec:other DB=postgresql
|
||||
- TASK=jasmine:ci DB=postgresql
|
||||
before_install:
|
||||
- sudo apt-get install libicu-dev -y
|
||||
install:
|
||||
- "travis_retry bundle install --deployment --without production --retry 5"
|
||||
branches:
|
||||
only:
|
||||
- 'master'
|
||||
rvm:
|
||||
- 2.0.0
|
||||
services:
|
||||
- redis-server
|
||||
before_script:
|
||||
- "cp config/database.yml.$DB config/database.yml"
|
||||
- "cp config/gitlab.yml.example config/gitlab.yml"
|
||||
- "bundle exec rake db:setup"
|
||||
- "bundle exec rake db:seed_fu"
|
||||
script: "bundle exec rake $TASK --trace"
|
||||
notifications:
|
||||
email: false
|
||||
@@ -1,5 +1,56 @@
|
||||
v 7.4.0
|
||||
- Refactored membership logic
|
||||
- Fix creating new files with web editor
|
||||
- Improve error reporting on users API (Julien Bianchi)
|
||||
- Refactor test coverage tools usage. Use SIMPLECOV=true to generate it locally
|
||||
- Increase unicorn timeout to 60 seconds
|
||||
- Sort search autocomplete projects by stars count so most popular go first
|
||||
|
||||
v 7.3.1
|
||||
- Fix ref parsing in Gitlab::GitAccess
|
||||
- Fix error 500 when viewing diff on a file with changed permissions
|
||||
- Fix adding comments to MR when source branch is master
|
||||
- Fix error 500 when searching description contains relative link
|
||||
|
||||
v 7.3.0
|
||||
- Always set the 'origin' remote in satellite actions
|
||||
- Write authorized_keys in tmp/ during tests
|
||||
- Use sockets to connect to Redis
|
||||
- Add dormant New Relic gem (can be enabled via environment variables)
|
||||
- Expire Rack sessions after 1 week
|
||||
- Cleaner signin/signup pages
|
||||
- Improved comments UI
|
||||
- Better search with filtering, pagination etc
|
||||
- Added a checkbox to toggle line wrapping in diff (Yuriy Glukhov)
|
||||
- Prevent project stars duplication when fork project
|
||||
- Use the default Unicorn socket backlog value of 1024
|
||||
- Support Unix domain sockets for Redis
|
||||
- Store session Redis keys in 'session:gitlab:' namespace
|
||||
- Deprecate LDAP account takeover based on partial LDAP email / GitLab username match
|
||||
- Use /bin/sh instead of Bash in bin/web, bin/background_jobs (Pavel Novitskiy)
|
||||
- Keyboard shortcuts for productivity (Robert Schilling)
|
||||
- API: filter issues by state (Julien Bianchi)
|
||||
- API: filter issues by labels (Julien Bianchi)
|
||||
- Add system hook for ssh key changes
|
||||
- Add blob permalink link (Ciro Santilli)
|
||||
- Create annotated tags through UI and API (Sean Edge)
|
||||
- Snippets search (Charles Bushong)
|
||||
- Comment new push to existing MR
|
||||
- Add 'ci' to the blacklist of forbidden names
|
||||
- Improve text filtering on issues page
|
||||
- Comment & Close button
|
||||
- Process git push --all much faster
|
||||
- Don't allow edit of system notes
|
||||
- Project wiki search (Ralf Seidler)
|
||||
- Enabled Shibboleth authentication support (Matus Banas)
|
||||
- Zen mode (fullscreen) for issues/MR/notes (Robert Schilling)
|
||||
- Add ability to configure webhook timeout via gitlab.yml (Wes Gurney)
|
||||
- Sort project merge requests in asc or desc order for updated_at or created_at field (sponsored by O'Reilly Media)
|
||||
- Add Redis socket support to 'rake gitlab:shell:install'
|
||||
|
||||
v 7.2.1
|
||||
- Delete orphaned labels during label migration (James Brooks)
|
||||
- Security: prevent XSS with stricter MIME types for raw repo files
|
||||
|
||||
v 7.2.0
|
||||
- Explore page
|
||||
|
||||
+7
-6
@@ -44,7 +44,7 @@ Please send a merge request with a tested solution or a merge request with a fai
|
||||
1. **Output of checks**
|
||||
* Results of GitLab [Application Check](doc/install/installation.md#check-application-status) (`sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production SANITIZE=true`); we will only investigate if the tests are passing
|
||||
* Version of GitLab you are running; we will only investigate issues in the latest stable and development releases as per the [maintenance policy](MAINTENANCE.md)
|
||||
* Add the last commit sha1 of the GitLab version you used to replicate the issue (obtainable from the help page)
|
||||
* Add the last commit SHA-1 of the GitLab version you used to replicate the issue (obtainable from the help page)
|
||||
* Describe your setup (use relevant parts from `sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production`)
|
||||
1. **Possible fixes**: If you can, link to the line of code that might be responsible for the problem
|
||||
|
||||
@@ -62,6 +62,7 @@ If you can, please submit a merge request with the fix or improvements including
|
||||
1. Create a feature branch
|
||||
1. Write [tests](README.md#run-the-tests) and code
|
||||
1. Add your changes to the [CHANGELOG](CHANGELOG) insert your line at a [random point](doc/workflow/gitlab_flow.md#do-not-order-commits-with-rebase) in the current version
|
||||
1. If you are changing the README, some documentation or other things which have no effect on the tests, add `[ci skip]` somewhere in the commit message
|
||||
1. If you have multiple commits please combine them into one commit by [squashing them](http://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)
|
||||
1. Push the commit to your fork
|
||||
1. Submit a merge request (MR) to the master branch
|
||||
@@ -75,7 +76,7 @@ If you can, please submit a merge request with the fix or improvements including
|
||||
|
||||
The **official merge window** is in the beginning of the month from the 1st to the 7th day of the month. The best time to submit a MR and get feedback fast. Before this time the GitLab B.V. team is still dealing with work that is created by the monthly release such as assisting subscribers with upgrade issues, the release of Enterprise Edition and the upgrade of GitLab Cloud. After the 7th it is already getting closer to the release date of the next version. This means there is less time to fix the issues created by merging large new features.
|
||||
|
||||
Please keep the change in a single MR **as small as possible**. If you want to contribute a large feature think very hard what the minimum viable change is. Can you split functionality? Can you only submit the backend/API code? Can you start with a very simple UI? Can you do part of the refactor? The increased reviewability of small MR's that leads to higher code quality is more important to us than having a mimimal commit log. The smaller a MR is the more likely it is it will be merged (quickly), after that you can send more MR's to enhance it.
|
||||
Please keep the change in a single MR **as small as possible**. If you want to contribute a large feature think very hard what the minimum viable change is. Can you split functionality? Can you only submit the backend/API code? Can you start with a very simple UI? Can you do part of the refactor? The increased reviewability of small MR's that leads to higher code quality is more important to us than having a minimal commit log. The smaller a MR is the more likely it is it will be merged (quickly), after that you can send more MR's to enhance it.
|
||||
|
||||
For examples of feedback on merge requests please look at already [closed merge requests](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests?assignee_id=&label_name=&milestone_id=&scope=&sort=&state=closed). If you would like quick feedback on your merge request feel free to mention one of the Merge Marshalls of [the core-team](https://about.gitlab.com/core-team/). Please ensure that your merge request meets the following contribution acceptance criteria.
|
||||
|
||||
@@ -85,19 +86,19 @@ For examples of feedback on merge requests please look at already [closed merge
|
||||
1. Are there points in the code the reviewer needs to double check?
|
||||
1. Why was this MR needed?
|
||||
1. What are the relevant issue numbers / [Feature requests](http://feedback.gitlab.com/)?
|
||||
1. Screenshots (If appropiate)
|
||||
1. Screenshots (If appropriate)
|
||||
|
||||
## Contribution acceptance criteria
|
||||
|
||||
1. The change is as small as possible (see the above paragraph for details)
|
||||
1. Include proper tests and make all tests pass (unless it contains a test exposing a bug in existing code)
|
||||
1. Can merge without problems (if not please use: `git rebase master`)
|
||||
1. Initially contains a single commit (please use `git rebase -i` to squash commits)
|
||||
1. Can merge without problems (if not please merge `master`, never rebase commits pushed to the remote server)
|
||||
1. Does not break any existing functionality
|
||||
1. Fixes one specific issue or implements one specific feature (do not combine things, send separate merge requests if needed)
|
||||
1. Keeps the GitLab code base clean and well structured
|
||||
1. Contains functionality we think other users will benefit from too
|
||||
1. Doesn't add configuration options since they complicate future changes
|
||||
1. Initially contains a single commit (please use `git rebase -i` to squash commits)
|
||||
1. Changes after submitting the merge request should be in separate commits (no squashing). You will be asked to squash when the review is over, before merging.
|
||||
1. It conforms to the following style guides
|
||||
|
||||
@@ -114,4 +115,4 @@ For examples of feedback on merge requests please look at already [closed merge
|
||||
1. [Shell commands](doc/development/shell_commands.md) created by GitLab contributors to enhance security
|
||||
1. [Markdown](http://www.cirosantilli.com/markdown-styleguide)
|
||||
|
||||
This is also the style used by linting tools such as [Rubocop](https://github.com/bbatsov/rubocop), [PullReview](https://www.pullreview.com/) and [Hound CI](https://houndci.com).
|
||||
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).
|
||||
|
||||
@@ -1 +1 @@
|
||||
1.9.7
|
||||
2.0.0
|
||||
|
||||
@@ -27,16 +27,17 @@ gem 'omniauth', "~> 1.1.3"
|
||||
gem 'omniauth-google-oauth2'
|
||||
gem 'omniauth-twitter'
|
||||
gem 'omniauth-github'
|
||||
gem 'omniauth-shibboleth'
|
||||
|
||||
# Extracting information from a git repository
|
||||
# Provide access to Gitlab::Git library
|
||||
gem "gitlab_git", '~> 6.0'
|
||||
gem "gitlab_git", '7.0.0.rc3'
|
||||
|
||||
# Ruby/Rack Git Smart-HTTP Server Handler
|
||||
gem 'gitlab-grack', '~> 2.0.0.pre', require: 'grack'
|
||||
|
||||
# LDAP Auth
|
||||
gem 'gitlab_omniauth-ldap', '1.0.4', require: "omniauth-ldap"
|
||||
gem 'gitlab_omniauth-ldap', '1.1.0', require: "omniauth-ldap"
|
||||
|
||||
# Git Wiki
|
||||
gem 'gollum-lib', '~> 3.0.0'
|
||||
@@ -82,7 +83,7 @@ gem "seed-fu"
|
||||
gem "github-markup"
|
||||
|
||||
# Required markup gems by github-markdown
|
||||
gem 'redcarpet', '~> 2.2.2'
|
||||
gem 'redcarpet', '~> 3.1.2'
|
||||
gem 'RedCloth'
|
||||
gem 'rdoc', '~>3.6'
|
||||
gem 'org-ruby'
|
||||
@@ -156,6 +157,9 @@ gem "rack-attack"
|
||||
# Ace editor
|
||||
gem 'ace-rails-ap'
|
||||
|
||||
# Keyboard shortcuts
|
||||
gem 'mousetrap-rails'
|
||||
|
||||
# Semantic UI Sass for Sidebar
|
||||
gem 'semantic-ui-sass', '~> 0.16.1.0'
|
||||
|
||||
@@ -231,7 +235,7 @@ group :development, :test do
|
||||
|
||||
gem 'jasmine', '2.0.2'
|
||||
|
||||
gem "spring", '1.1.1'
|
||||
gem "spring", '1.1.3'
|
||||
gem "spring-commands-rspec", '1.0.1'
|
||||
gem "spring-commands-spinach", '1.0.0'
|
||||
end
|
||||
@@ -247,3 +251,5 @@ end
|
||||
group :production do
|
||||
gem "gitlab_meta", '7.0'
|
||||
end
|
||||
|
||||
gem "newrelic_rpm"
|
||||
|
||||
+22
-14
@@ -106,7 +106,7 @@ GEM
|
||||
devise (~> 3.2)
|
||||
diff-lcs (1.2.5)
|
||||
diffy (3.0.3)
|
||||
docile (1.1.1)
|
||||
docile (1.1.5)
|
||||
dotenv (0.9.0)
|
||||
dropzonejs-rails (0.4.14)
|
||||
rails (> 3.1)
|
||||
@@ -168,7 +168,7 @@ GEM
|
||||
multi_json
|
||||
gitlab-grack (2.0.0.pre)
|
||||
rack (~> 1.5.1)
|
||||
gitlab-grit (2.6.10)
|
||||
gitlab-grit (2.6.11)
|
||||
charlock_holmes (~> 0.6)
|
||||
diff-lcs (~> 1.1)
|
||||
mime-types (~> 1.15)
|
||||
@@ -179,15 +179,15 @@ GEM
|
||||
mime-types (~> 1.19)
|
||||
gitlab_emoji (0.0.1.1)
|
||||
emoji (~> 1.0.1)
|
||||
gitlab_git (6.2.1)
|
||||
gitlab_git (7.0.0.rc3)
|
||||
activesupport (~> 4.0)
|
||||
charlock_holmes (~> 0.6)
|
||||
gitlab-grit (~> 2.6)
|
||||
gitlab-linguist (~> 3.0)
|
||||
rubyzip (~> 1.1)
|
||||
rugged (~> 0.21.0)
|
||||
gitlab_meta (7.0)
|
||||
gitlab_omniauth-ldap (1.0.4)
|
||||
net-ldap (~> 0.3.1)
|
||||
gitlab_omniauth-ldap (1.1.0)
|
||||
net-ldap (~> 0.7.0)
|
||||
omniauth (~> 1.0)
|
||||
pyu-ruby-sasl (~> 0.0.3.1)
|
||||
rubyntlm (~> 0.1.1)
|
||||
@@ -287,14 +287,16 @@ GEM
|
||||
mime-types (1.25.1)
|
||||
mini_portile (0.6.0)
|
||||
minitest (5.3.5)
|
||||
mousetrap-rails (1.4.6)
|
||||
multi_json (1.10.1)
|
||||
multi_xml (0.5.5)
|
||||
multipart-post (1.2.0)
|
||||
mysql2 (0.3.16)
|
||||
net-ldap (0.3.1)
|
||||
net-ldap (0.7.0)
|
||||
net-scp (1.1.2)
|
||||
net-ssh (>= 2.6.5)
|
||||
net-ssh (2.8.0)
|
||||
newrelic_rpm (3.9.4.245)
|
||||
nokogiri (1.6.2.1)
|
||||
mini_portile (= 0.6.0)
|
||||
nprogress-rails (0.1.2.3)
|
||||
@@ -320,6 +322,8 @@ GEM
|
||||
omniauth-oauth2 (1.1.1)
|
||||
oauth2 (~> 0.8.0)
|
||||
omniauth (~> 1.0)
|
||||
omniauth-shibboleth (1.1.1)
|
||||
omniauth (>= 1.0.0)
|
||||
omniauth-twitter (1.0.1)
|
||||
multi_json (~> 1.3)
|
||||
omniauth-oauth (~> 1.0)
|
||||
@@ -391,7 +395,7 @@ GEM
|
||||
ffi (>= 0.5.0)
|
||||
rdoc (3.12.2)
|
||||
json (~> 1.4)
|
||||
redcarpet (2.2.2)
|
||||
redcarpet (3.1.2)
|
||||
redis (3.0.6)
|
||||
redis-actionpack (4.0.0)
|
||||
actionpack (~> 4)
|
||||
@@ -436,6 +440,7 @@ GEM
|
||||
ruby-progressbar (1.2.0)
|
||||
rubyntlm (0.1.1)
|
||||
rubypants (0.2.0)
|
||||
rubyzip (1.1.6)
|
||||
rugged (0.21.0)
|
||||
safe_yaml (0.9.7)
|
||||
sanitize (2.1.0)
|
||||
@@ -467,7 +472,7 @@ GEM
|
||||
redis (>= 3.0.4)
|
||||
redis-namespace (>= 1.3.1)
|
||||
simple_oauth (0.1.9)
|
||||
simplecov (0.8.2)
|
||||
simplecov (0.9.0)
|
||||
docile (~> 1.1.0)
|
||||
multi_json
|
||||
simplecov-html (~> 0.8.0)
|
||||
@@ -489,7 +494,7 @@ GEM
|
||||
capybara (>= 2.0.0)
|
||||
railties (>= 3)
|
||||
spinach (>= 0.4)
|
||||
spring (1.1.1)
|
||||
spring (1.1.3)
|
||||
spring-commands-rspec (1.0.1)
|
||||
spring (>= 0.9.1)
|
||||
spring-commands-spinach (1.0.0)
|
||||
@@ -613,9 +618,9 @@ DEPENDENCIES
|
||||
gitlab-grack (~> 2.0.0.pre)
|
||||
gitlab-linguist (~> 3.0.0)
|
||||
gitlab_emoji (~> 0.0.1.1)
|
||||
gitlab_git (~> 6.0)
|
||||
gitlab_git (= 7.0.0.rc3)
|
||||
gitlab_meta (= 7.0)
|
||||
gitlab_omniauth-ldap (= 1.0.4)
|
||||
gitlab_omniauth-ldap (= 1.1.0)
|
||||
gollum-lib (~> 3.0.0)
|
||||
gon (~> 5.0.0)
|
||||
grape (~> 0.6.1)
|
||||
@@ -636,11 +641,14 @@ DEPENDENCIES
|
||||
launchy
|
||||
letter_opener
|
||||
minitest (~> 5.3.0)
|
||||
mousetrap-rails
|
||||
mysql2
|
||||
newrelic_rpm
|
||||
nprogress-rails
|
||||
omniauth (~> 1.1.3)
|
||||
omniauth-github
|
||||
omniauth-google-oauth2
|
||||
omniauth-shibboleth
|
||||
omniauth-twitter
|
||||
org-ruby
|
||||
pg
|
||||
@@ -657,7 +665,7 @@ DEPENDENCIES
|
||||
rb-fsevent
|
||||
rb-inotify
|
||||
rdoc (~> 3.6)
|
||||
redcarpet (~> 2.2.2)
|
||||
redcarpet (~> 3.1.2)
|
||||
redis-rails
|
||||
request_store
|
||||
rspec-rails
|
||||
@@ -676,7 +684,7 @@ DEPENDENCIES
|
||||
slack-notifier (~> 0.3.2)
|
||||
slim
|
||||
spinach-rails
|
||||
spring (= 1.1.1)
|
||||
spring (= 1.1.3)
|
||||
spring-commands-rspec (= 1.0.1)
|
||||
spring-commands-spinach (= 1.0.0)
|
||||
stamp
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# A sample Guardfile
|
||||
# More info at https://github.com/guard/guard#readme
|
||||
|
||||
guard 'rspec', cmd: "spring rspec", version: 2, all_on_start: false, all_after_pass: false do
|
||||
guard 'rspec', cmd: "spring rspec", all_on_start: false, all_after_pass: false do
|
||||
watch(%r{^spec/.+_spec\.rb$})
|
||||
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
||||
watch(%r{^lib/api/(.+)\.rb$}) { |m| "spec/requests/api/#{m[1]}_spec.rb" }
|
||||
@@ -19,7 +19,7 @@ guard 'rspec', cmd: "spring rspec", version: 2, all_on_start: false, all_after_p
|
||||
watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/requests/#{m[1]}_spec.rb" }
|
||||
end
|
||||
|
||||
guard 'spinach' do
|
||||
guard 'spinach', command_prefix: 'spring' do
|
||||
watch(%r|^features/(.*)\.feature|)
|
||||
watch(%r|^features/steps/(.*)([^/]+)\.rb|) do |m|
|
||||
"features/#{m[1]}#{m[2]}.feature"
|
||||
|
||||
+1
-1
@@ -87,7 +87,7 @@ Please use ``` to format console output, logs, and code as it's very hard to rea
|
||||
|
||||
### Issue fixed in newer version
|
||||
|
||||
Thanks for the issue report. This issue has already been fixed in newer versions of GitLab. Due to the size of this project and our limited resources we are only able to support the latest stable release as outlined in our \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker). In order to get this bug fix and enjoy many new features please \[upgrade\]\(https://github.com/gitlabhq/gitlabhq/tree/master/doc/update). If you still experience issues at that time please open a new issue following our issue tracker guidelines found in the \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines).
|
||||
Thanks for the issue report. This issue has already been fixed in newer versions of GitLab. Due to the size of this project and our limited resources we are only able to support the latest stable release as outlined in our \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker). In order to get this bug fix and enjoy many new features please \[upgrade\]\(https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update). If you still experience issues at that time please open a new issue following our issue tracker guidelines found in the \[contributing guidelines\]\(https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#issue-tracker-guidelines).
|
||||
|
||||
### Improperly formatted merge request
|
||||
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
# GitLab
|
||||
#  GitLab
|
||||
|
||||
## Open source software to collaborate on code
|
||||
|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
- Manage Git repositories with fine grained access controls that keep your code secure
|
||||
- Perform code reviews and enhance collaboration with merge requests
|
||||
@@ -21,7 +19,9 @@
|
||||
|
||||
- [](https://ci.gitlab.org/projects/1?ref=master) on ci.gitlab.org (master branch)
|
||||
|
||||
- [](https://codeclimate.com/github/gitlabhq/gitlabhq)
|
||||
- [](https://semaphoreapp.com/gitlabhq/gitlabhq)
|
||||
|
||||
- [](https://codeclimate.com/github/gitlabhq/gitlabhq)
|
||||
|
||||
- [](https://coveralls.io/r/gitlabhq/gitlabhq)
|
||||
|
||||
@@ -29,14 +29,14 @@
|
||||
|
||||
## Website
|
||||
|
||||
On [www.gitlab.com](https://www.gitlab.com/) you can find more information about:
|
||||
On [about.gitlab.com](https://about.gitlab.com/) you can find more information about:
|
||||
|
||||
- [Subscriptions](https://www.gitlab.com/subscription/)
|
||||
- [Consultancy](https://www.gitlab.com/consultancy/)
|
||||
- [Community](https://www.gitlab.com/community/)
|
||||
- [Hosted GitLab.com](https://www.gitlab.com/gitlab-com/) use GitLab as a free service
|
||||
- [GitLab Enterprise Edition](https://www.gitlab.com/gitlab-ee/) with additional features aimed at larger organizations.
|
||||
- [GitLab CI](https://www.gitlab.com/gitlab-ci/) a continuous integration (CI) server that is easy to integrate with GitLab.
|
||||
- [Subscriptions](https://about.gitlab.com/subscription/)
|
||||
- [Consultancy](https://about.gitlab.com/consultancy/)
|
||||
- [Community](https://about.gitlab.com/community/)
|
||||
- [Hosted GitLab.com](https://about.gitlab.com/gitlab-com/) use GitLab as a free service
|
||||
- [GitLab Enterprise Edition](https://about.gitlab.com/gitlab-ee/) with additional features aimed at larger organizations.
|
||||
- [GitLab CI](https://about.gitlab.com/gitlab-ci/) a continuous integration (CI) server that is easy to integrate with GitLab.
|
||||
|
||||
## Third-party applications
|
||||
|
||||
@@ -61,11 +61,11 @@ These applications are maintained by contributors, GitLab B.V. does not offer su
|
||||
|
||||
## Installation
|
||||
|
||||
Please see [the installation page on the GitLab website](https://www.gitlab.com/installation/).
|
||||
Please see [the installation page on the GitLab website](https://about.gitlab.com/installation/).
|
||||
|
||||
### New versions
|
||||
|
||||
Since 2011 a minor or major version of GitLab is released on the 22nd of every month. Patch and security releases come out when needed. New features are detailed on the [blog](https://www.gitlab.com/blog/) and in the [changelog](CHANGELOG). For more information about the release process see the release [documentation](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/release). Features that will likely be in the next releases can be found on the [feature request forum](http://feedback.gitlab.com/forums/176466-general) with the status [started](http://feedback.gitlab.com/forums/176466-general/status/796456) and [completed](http://feedback.gitlab.com/forums/176466-general/status/796457).
|
||||
Since 2011 a minor or major version of GitLab is released on the 22nd of every month. Patch and security releases come out when needed. New features are detailed on the [blog](https://about.gitlab.com/blog/) and in the [changelog](CHANGELOG). For more information about the release process see the release [documentation](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/release). Features that will likely be in the next releases can be found on the [feature request forum](http://feedback.gitlab.com/forums/176466-general) with the status [started](http://feedback.gitlab.com/forums/176466-general/status/796456) and [completed](http://feedback.gitlab.com/forums/176466-general/status/796457).
|
||||
|
||||
### Upgrading
|
||||
|
||||
@@ -85,7 +85,8 @@ Please login with `root` / `5iveL!fe`
|
||||
|
||||
## Install a development environment
|
||||
|
||||
We recommend setting up your development environment with [the cookbook](https://gitlab.com/gitlab-org/cookbook-gitlab/blob/master/README.md#installation). If you do not use the cookbook you might need to copy the example development unicorn configuration file
|
||||
We recommend setting up your development environment with [the GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit).
|
||||
If you do not use the development kit you might need to copy the example development unicorn configuration file
|
||||
|
||||
cp config/unicorn.rb.example.development config/unicorn.rb
|
||||
|
||||
@@ -126,7 +127,7 @@ All documentation can be found on [doc.gitlab.com/ce/](http://doc.gitlab.com/ce/
|
||||
|
||||
## Getting help
|
||||
|
||||
Please see [Getting help for GitLab](https://www.gitlab.com/getting-help/) on our website for the many options to get help.
|
||||
Please see [Getting help for GitLab](https://about.gitlab.com/getting-help/) on our website for the many options to get help.
|
||||
|
||||
## Is it any good?
|
||||
|
||||
|
||||
@@ -46,10 +46,10 @@ class Admin
|
||||
modal.hide()
|
||||
$('.change-owner-link').show()
|
||||
|
||||
$('li.users_project').bind 'ajax:success', ->
|
||||
$('li.project_member').bind 'ajax:success', ->
|
||||
Turbolinks.visit(location.href)
|
||||
|
||||
$('li.users_group').bind 'ajax:success', ->
|
||||
$('li.group_member').bind 'ajax:success', ->
|
||||
Turbolinks.visit(location.href)
|
||||
|
||||
@Admin = Admin
|
||||
|
||||
@@ -15,22 +15,31 @@
|
||||
#= require jquery.atwho
|
||||
#= require jquery.scrollTo
|
||||
#= require jquery.blockUI
|
||||
#= require turbolinks
|
||||
#= require jquery.turbolinks
|
||||
#= require turbolinks
|
||||
#= require bootstrap
|
||||
#= require select2
|
||||
#= require raphael
|
||||
#= require g.raphael-min
|
||||
#= require g.bar-min
|
||||
#= require chart-lib.min
|
||||
#= require branch-graph
|
||||
#= require highlight.pack
|
||||
#= require ace/ace
|
||||
#= require ace/ext-searchbox
|
||||
#= require d3
|
||||
#= require underscore
|
||||
#= require nprogress
|
||||
#= require nprogress-turbolinks
|
||||
#= require dropzone
|
||||
#= require semantic-ui/sidebar
|
||||
#= require mousetrap
|
||||
#= require mousetrap/pause
|
||||
#= require shortcuts
|
||||
#= require shortcuts_navigation
|
||||
#= require shortcuts_dashboard_navigation
|
||||
#= require shortcuts_issueable
|
||||
#= require shortcuts_network
|
||||
#= require_tree .
|
||||
|
||||
window.slugify = (text) ->
|
||||
@@ -117,6 +126,13 @@ $ ->
|
||||
# Initialize select2 selects
|
||||
$('select.select2').select2(width: 'resolve', dropdownAutoWidth: true)
|
||||
|
||||
# Close select2 on escape
|
||||
$('.js-select2').bind 'select2-close', ->
|
||||
setTimeout ( ->
|
||||
$('.select2-container-active').removeClass('select2-container-active')
|
||||
$(':focus').blur()
|
||||
), 1
|
||||
|
||||
# Initialize tooltips
|
||||
$('.has_tooltip').tooltip()
|
||||
|
||||
@@ -134,7 +150,6 @@ $ ->
|
||||
if (flash = $(".flash-container")).length > 0
|
||||
flash.click -> $(@).fadeOut()
|
||||
flash.show()
|
||||
setTimeout (-> flash.fadeOut()), 5000
|
||||
|
||||
# Disable form buttons while a form is submitting
|
||||
$('body').on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) ->
|
||||
@@ -149,20 +164,6 @@ $ ->
|
||||
# Show/Hide the profile menu when hovering the account box
|
||||
$('.account-box').hover -> $(@).toggleClass('hover')
|
||||
|
||||
# Focus search field by pressing 's' key
|
||||
$(document).keypress (e) ->
|
||||
# Don't do anything if typing in an input
|
||||
return if $(e.target).is(":input")
|
||||
|
||||
switch e.which
|
||||
when 115
|
||||
$("#search").focus()
|
||||
e.preventDefault()
|
||||
when 63
|
||||
new Shortcuts()
|
||||
e.preventDefault()
|
||||
|
||||
|
||||
# Commit show suppressed diff
|
||||
$(".diff-content").on "click", ".supp_diff_link", ->
|
||||
$(@).next('table').show()
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
$ ->
|
||||
# Toggle line wrapping in diff.
|
||||
#
|
||||
# %div.diff-file
|
||||
# %input.js-toggle-diff-line-wrap
|
||||
# %td.line_content
|
||||
#
|
||||
$("body").on "click", ".js-toggle-diff-line-wrap", (e) ->
|
||||
diffFile = $(@).closest(".diff-file")
|
||||
if $(@).is(":checked")
|
||||
diffFile.addClass("diff-wrap-lines")
|
||||
else
|
||||
diffFile.removeClass("diff-wrap-lines")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
class BranchGraph
|
||||
class @BranchGraph
|
||||
constructor: (@element, @options) ->
|
||||
@preparedCommits = {}
|
||||
@mtime = 0
|
||||
@@ -90,11 +90,15 @@ class BranchGraph
|
||||
|
||||
renderPartialGraph: ->
|
||||
start = Math.floor((@element.scrollTop() - @offsetY) / @unitTime) - 10
|
||||
start = 0 if start < 0
|
||||
if start < 0
|
||||
isGraphEdge = true
|
||||
start = 0
|
||||
end = start + 40
|
||||
end = @commits.length if @commits.length < end
|
||||
if @commits.length < end
|
||||
isGraphEdge = true
|
||||
end = @commits.length
|
||||
|
||||
if @prev_start == -1 or Math.abs(@prev_start - start) > 10
|
||||
if @prev_start == -1 or Math.abs(@prev_start - start) > 10 or isGraphEdge
|
||||
i = start
|
||||
|
||||
@prev_start = start
|
||||
@@ -120,23 +124,32 @@ class BranchGraph
|
||||
@top.toFront()
|
||||
|
||||
bindEvents: ->
|
||||
drag = {}
|
||||
element = @element
|
||||
|
||||
$(element).scroll (event) =>
|
||||
@renderPartialGraph()
|
||||
|
||||
$(window).on
|
||||
keydown: (event) =>
|
||||
# left
|
||||
element.scrollLeft element.scrollLeft() - 50 if event.keyCode is 37
|
||||
# top
|
||||
element.scrollTop element.scrollTop() - 50 if event.keyCode is 38
|
||||
# right
|
||||
element.scrollLeft element.scrollLeft() + 50 if event.keyCode is 39
|
||||
# bottom
|
||||
element.scrollTop element.scrollTop() + 50 if event.keyCode is 40
|
||||
@renderPartialGraph()
|
||||
scrollDown: =>
|
||||
@element.scrollTop @element.scrollTop() + 50
|
||||
@renderPartialGraph()
|
||||
|
||||
scrollUp: =>
|
||||
@element.scrollTop @element.scrollTop() - 50
|
||||
@renderPartialGraph()
|
||||
|
||||
scrollLeft: =>
|
||||
@element.scrollLeft @element.scrollLeft() - 50
|
||||
@renderPartialGraph()
|
||||
|
||||
scrollRight: =>
|
||||
@element.scrollLeft @element.scrollLeft() + 50
|
||||
@renderPartialGraph()
|
||||
|
||||
scrollBottom: =>
|
||||
@element.scrollTop @element.find('svg').height()
|
||||
|
||||
scrollTop: =>
|
||||
@element.scrollTop 0
|
||||
|
||||
appendLabel: (x, y, commit) ->
|
||||
return unless commit.refs
|
||||
@@ -325,5 +338,3 @@ Raphael::textWrap = (t, width) ->
|
||||
b = t.getBBox()
|
||||
h = Math.abs(b.y2) - Math.abs(b.y) + 1
|
||||
t.attr y: b.y + h
|
||||
|
||||
@BranchGraph = BranchGraph
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
@Chart =
|
||||
labels: []
|
||||
values: []
|
||||
|
||||
init: (labels, values, title) ->
|
||||
r = Raphael('activity-chart')
|
||||
|
||||
fin = ->
|
||||
@flag = r.popup(@bar.x, @bar.y, @bar.value or "0").insertBefore(this)
|
||||
|
||||
fout = ->
|
||||
@flag.animate
|
||||
opacity: 0, 300, -> @remove()
|
||||
|
||||
r.text(160, 10, title).attr font: "13px sans-serif"
|
||||
r.barchart(
|
||||
10, 20, 560, 200,
|
||||
[values],
|
||||
{colors:["#456"]}
|
||||
).label(labels, true)
|
||||
.hover(fin, fout)
|
||||
@@ -15,50 +15,88 @@ class Dispatcher
|
||||
return false
|
||||
|
||||
path = page.split(':')
|
||||
shortcut_handler = null
|
||||
|
||||
switch page
|
||||
when 'projects:issues:index'
|
||||
Issues.init()
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
when 'projects:issues:show'
|
||||
new Issue()
|
||||
shortcut_handler = new ShortcutsIssueable()
|
||||
new ZenMode()
|
||||
when 'projects:milestones:show'
|
||||
new Milestone()
|
||||
when 'projects:issues:new'
|
||||
when 'projects:issues:new','projects:issues:edit'
|
||||
GitLab.GfmAutoComplete.setup()
|
||||
when 'projects:merge_requests:new'
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
new ZenMode()
|
||||
when 'projects:merge_requests:new', 'projects:merge_requests:edit'
|
||||
GitLab.GfmAutoComplete.setup()
|
||||
new Diff()
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
new ZenMode()
|
||||
when 'projects:merge_requests:show'
|
||||
new Diff()
|
||||
shortcut_handler = new ShortcutsIssueable()
|
||||
new ZenMode()
|
||||
when "projects:merge_requests:diffs"
|
||||
new Diff()
|
||||
when 'projects:merge_requests:index'
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
when 'dashboard:show'
|
||||
new Dashboard()
|
||||
new Activities()
|
||||
when 'projects:commit:show'
|
||||
new Commit()
|
||||
new Diff()
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
when 'projects:commits:show'
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
when 'groups:show', 'projects:show'
|
||||
new Activities()
|
||||
when 'projects:new', 'projects:edit'
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
when 'projects:new'
|
||||
new Project()
|
||||
when 'projects:edit'
|
||||
new Project()
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
when 'projects:teams:members:index'
|
||||
new TeamMembers()
|
||||
when 'groups:members'
|
||||
new GroupMembers()
|
||||
when 'projects:tree:show'
|
||||
new TreeView()
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
when 'projects:blob:show'
|
||||
new BlobView()
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
when 'projects:labels:new', 'projects:labels:edit'
|
||||
new Labels()
|
||||
when 'projects:network:show'
|
||||
# Ensure we don't create a particular shortcut handler here. This is
|
||||
# already created, where the network graph is created.
|
||||
shortcut_handler = true
|
||||
|
||||
switch path.first()
|
||||
when 'admin' then new Admin()
|
||||
when 'dashboard'
|
||||
shortcut_handler = new ShortcutsDashboardNavigation()
|
||||
when 'projects'
|
||||
new Wikis() if path[1] == 'wikis'
|
||||
switch path[1]
|
||||
when 'wikis'
|
||||
new Wikis()
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
when 'snippets', 'labels', 'graphs'
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
when 'team_members', 'deploy_keys', 'hooks', 'services', 'protected_branches'
|
||||
shortcut_handler = new ShortcutsNavigation()
|
||||
|
||||
|
||||
# If we haven't installed a custom shortcut handler, install the default one
|
||||
if not shortcut_handler
|
||||
new Shortcuts()
|
||||
|
||||
initSearch: ->
|
||||
opts = $('.search-autocomplete-opts')
|
||||
path = opts.data('autocomplete-path')
|
||||
|
||||
@@ -10,6 +10,5 @@ class Flash
|
||||
|
||||
flash.click -> $(@).fadeOut()
|
||||
flash.show()
|
||||
setTimeout (-> flash.fadeOut()), 5000
|
||||
|
||||
@Flash = Flash
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
class GroupMembers
|
||||
constructor: ->
|
||||
$('li.users_group').bind 'ajax:success', ->
|
||||
$('li.group_member').bind 'ajax:success', ->
|
||||
$(this).fadeOut()
|
||||
|
||||
@GroupMembers = GroupMembers
|
||||
|
||||
@@ -43,25 +43,31 @@
|
||||
|
||||
$(".selected_issue").bind "change", Issues.checkChanged
|
||||
|
||||
|
||||
# Make sure we trigger ajax request only after user stop typing
|
||||
initSearch: ->
|
||||
form = $("#issue_search_form")
|
||||
last_terms = ""
|
||||
@timer = null
|
||||
$("#issue_search").keyup ->
|
||||
terms = $(this).val()
|
||||
unless terms is last_terms
|
||||
last_terms = terms
|
||||
if terms.length >= 2 or terms.length is 0
|
||||
$.ajax
|
||||
type: "GET"
|
||||
url: location.href
|
||||
data: "issue_search=" + terms
|
||||
complete: ->
|
||||
$(".loading").hide()
|
||||
success: (data) ->
|
||||
$('.issues-holder').html(data.html)
|
||||
Issues.reload()
|
||||
dataType: "json"
|
||||
clearTimeout(@timer);
|
||||
@timer = setTimeout(Issues.filterResults, 500)
|
||||
|
||||
filterResults: =>
|
||||
form = $("#issue_search_form")
|
||||
search = $("#issue_search").val()
|
||||
$('.issues-holder').css("opacity", '0.5')
|
||||
issues_url = form.attr('action') + '? '+ form.serialize()
|
||||
|
||||
$.ajax
|
||||
type: "GET"
|
||||
url: form.attr('action')
|
||||
data: form.serialize()
|
||||
complete: ->
|
||||
$('.issues-holder').css("opacity", '1.0')
|
||||
success: (data) ->
|
||||
$('.issues-holder').html(data.html)
|
||||
# Change url so if user reload a page - search results are saved
|
||||
History.replaceState {page: issues_url}, document.title, issues_url
|
||||
Issues.reload()
|
||||
dataType: "json"
|
||||
|
||||
checkChanged: ->
|
||||
checked_issues = $(".selected_issue:checked")
|
||||
|
||||
@@ -27,7 +27,7 @@ $(document).ready ->
|
||||
dropzone = $(".div-dropzone").dropzone(
|
||||
url: project_image_path_upload
|
||||
dictDefaultMessage: ""
|
||||
clickable: true
|
||||
clickable: false
|
||||
paramName: "markdown_img"
|
||||
maxFilesize: 10
|
||||
uploadMultiple: false
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
class Network
|
||||
class @Network
|
||||
constructor: (opts) ->
|
||||
$("#filter_ref").click ->
|
||||
$(this).closest('form').submit()
|
||||
|
||||
branch_graph = new BranchGraph($(".network-graph"), opts)
|
||||
@branch_graph = new BranchGraph($(".network-graph"), opts)
|
||||
|
||||
vph = $(window).height() - 250
|
||||
$('.network-graph').css 'height': (vph + 'px')
|
||||
|
||||
@Network = Network
|
||||
|
||||
@@ -16,13 +16,19 @@ class Notes
|
||||
$(document).on "ajax:success", ".js-main-target-form", @addNote
|
||||
$(document).on "ajax:success", ".js-discussion-note-form", @addDiscussionNote
|
||||
|
||||
# change note in UI after update
|
||||
# change note in UI after update
|
||||
$(document).on "ajax:success", "form.edit_note", @updateNote
|
||||
|
||||
# Edit note link
|
||||
$(document).on "click", ".js-note-edit", @showEditForm
|
||||
$(document).on "click", ".note-edit-cancel", @cancelEdit
|
||||
|
||||
# Reopen and close actions for Issue/MR combined with note form submit
|
||||
$(document).on "click", ".js-note-target-reopen", @targetReopen
|
||||
$(document).on "click", ".js-note-target-close", @targetClose
|
||||
$(document).on "click", ".js-comment-button", @updateCloseButton
|
||||
$(document).on "keyup", ".js-note-text", @updateTargetButtons
|
||||
|
||||
# remove a note (in general)
|
||||
$(document).on "click", ".js-note-delete", @removeNote
|
||||
|
||||
@@ -78,7 +84,9 @@ class Notes
|
||||
$(document).off "click", ".js-add-diff-note-button"
|
||||
$(document).off "visibilitychange"
|
||||
$(document).off "keypress", @notes_forms
|
||||
|
||||
$(document).off "keyup", ".js-note-text"
|
||||
$(document).off "click", ".js-note-target-reopen"
|
||||
$(document).off "click", ".js-note-target-close"
|
||||
|
||||
initRefresh: ->
|
||||
clearInterval(Notes.interval)
|
||||
@@ -406,30 +414,6 @@ class Notes
|
||||
form.find(".js-note-text").focus()
|
||||
form.addClass "js-discussion-note-form"
|
||||
|
||||
###
|
||||
General note form setup.
|
||||
|
||||
deactivates the submit button when text is empty
|
||||
hides the preview button when text is empty
|
||||
setup GFM auto complete
|
||||
show the form
|
||||
###
|
||||
setupNoteForm: (form) =>
|
||||
disableButtonIfEmptyField form.find(".js-note-text"), form.find(".js-comment-button")
|
||||
form.removeClass "js-new-note-form"
|
||||
form.removeClass "js-new-note-form"
|
||||
GitLab.GfmAutoComplete.setup()
|
||||
|
||||
# setup preview buttons
|
||||
previewButton = form.find(".js-note-preview-button")
|
||||
form.find(".js-note-text").on "input", ->
|
||||
if $(this).val().trim() isnt ""
|
||||
previewButton.removeClass("turn-off").addClass "turn-on"
|
||||
else
|
||||
previewButton.removeClass("turn-on").addClass "turn-off"
|
||||
|
||||
form.show()
|
||||
|
||||
###
|
||||
Called when clicking on the "add a comment" button on the side of a diff line.
|
||||
|
||||
@@ -502,4 +486,33 @@ class Notes
|
||||
visibilityChange: =>
|
||||
@refresh()
|
||||
|
||||
targetReopen: (e) =>
|
||||
@submitNoteForm($(e.target).parents('form'))
|
||||
|
||||
targetClose: (e) =>
|
||||
@submitNoteForm($(e.target).parents('form'))
|
||||
|
||||
submitNoteForm: (form) =>
|
||||
noteText = form.find(".js-note-text").val()
|
||||
if noteText.trim().length > 0
|
||||
form.submit()
|
||||
|
||||
updateCloseButton: (e) =>
|
||||
textarea = $(e.target)
|
||||
form = textarea.parents('form')
|
||||
form.find('.js-note-target-close').text('Close')
|
||||
|
||||
updateTargetButtons: (e) =>
|
||||
textarea = $(e.target)
|
||||
form = textarea.parents('form')
|
||||
|
||||
if textarea.val().trim().length > 0
|
||||
form.find('.js-note-target-reopen').text('Comment & reopen')
|
||||
form.find('.js-note-target-close').text('Comment & close')
|
||||
else
|
||||
form.find('.js-note-target-reopen').text('Reopen')
|
||||
form.find('.js-note-target-close').text('Close')
|
||||
|
||||
|
||||
|
||||
@Notes = Notes
|
||||
|
||||
@@ -47,7 +47,7 @@ $ ->
|
||||
$(@).parents('.no-ssh-key-message').hide()
|
||||
e.preventDefault()
|
||||
|
||||
$('.project-side .star').on 'ajax:success', (e, data, status, xhr) ->
|
||||
$('.project-home-panel .star').on 'ajax:success', (e, data, status, xhr) ->
|
||||
$(@).toggleClass('on').find('.count').html(data.star_count)
|
||||
.on 'ajax:error', (e, xhr, status, error) ->
|
||||
new Flash('Star toggle failed. Try again later.', 'alert')
|
||||
|
||||
@@ -1,11 +1,30 @@
|
||||
class Shortcuts
|
||||
class @Shortcuts
|
||||
constructor: ->
|
||||
@enabledHelp = []
|
||||
Mousetrap.reset()
|
||||
Mousetrap.bind('?', @selectiveHelp)
|
||||
Mousetrap.bind('s', Shortcuts.focusSearch)
|
||||
|
||||
selectiveHelp: (e) =>
|
||||
Shortcuts.showHelp(e, @enabledHelp)
|
||||
|
||||
@showHelp: (e, location) ->
|
||||
if $('#modal-shortcuts').length > 0
|
||||
$('#modal-shortcuts').modal('show')
|
||||
else
|
||||
$.ajax(
|
||||
url: '/help/shortcuts',
|
||||
dataType: "script"
|
||||
dataType: 'script',
|
||||
success: (e) ->
|
||||
if location and location.length > 0
|
||||
for l in location
|
||||
$(l).show()
|
||||
else
|
||||
$('.hidden-shortcut').show()
|
||||
$('.js-more-help-button').remove()
|
||||
)
|
||||
e.preventDefault()
|
||||
|
||||
@Shortcuts = Shortcuts
|
||||
@focusSearch: (e) ->
|
||||
$('#search').focus()
|
||||
e.preventDefault()
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
#= require shortcuts
|
||||
|
||||
class @ShortcutsDashboardNavigation extends Shortcuts
|
||||
constructor: ->
|
||||
super()
|
||||
Mousetrap.bind('g a', -> ShortcutsDashboardNavigation.findAndollowLink('.shortcuts-activity'))
|
||||
Mousetrap.bind('g p', -> ShortcutsDashboardNavigation.findAndollowLink('.shortcuts-projects'))
|
||||
Mousetrap.bind('g i', -> ShortcutsDashboardNavigation.findAndollowLink('.shortcuts-issues'))
|
||||
Mousetrap.bind('g m', -> ShortcutsDashboardNavigation.findAndollowLink('.shortcuts-merge_requests'))
|
||||
|
||||
@findAndollowLink: (selector) ->
|
||||
link = $(selector).attr('href')
|
||||
if link
|
||||
window.location = link
|
||||
@@ -0,0 +1,19 @@
|
||||
#= require shortcuts_navigation
|
||||
|
||||
class @ShortcutsIssueable extends ShortcutsNavigation
|
||||
constructor: (isMergeRequest) ->
|
||||
super()
|
||||
Mousetrap.bind('a', ->
|
||||
$('.js-assignee').select2('open')
|
||||
return false
|
||||
)
|
||||
Mousetrap.bind('m', ->
|
||||
$('.js-milestone').select2('open')
|
||||
return false
|
||||
)
|
||||
|
||||
if isMergeRequest
|
||||
@enabledHelp.push('.hidden-shortcut.merge_reuests')
|
||||
else
|
||||
@enabledHelp.push('.hidden-shortcut.issues')
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
#= require shortcuts
|
||||
|
||||
class @ShortcutsNavigation extends Shortcuts
|
||||
constructor: ->
|
||||
super()
|
||||
Mousetrap.bind('g p', -> ShortcutsNavigation.findAndollowLink('.shortcuts-project'))
|
||||
Mousetrap.bind('g f', -> ShortcutsNavigation.findAndollowLink('.shortcuts-tree'))
|
||||
Mousetrap.bind('g c', -> ShortcutsNavigation.findAndollowLink('.shortcuts-commits'))
|
||||
Mousetrap.bind('g n', -> ShortcutsNavigation.findAndollowLink('.shortcuts-network'))
|
||||
Mousetrap.bind('g g', -> ShortcutsNavigation.findAndollowLink('.shortcuts-graphs'))
|
||||
Mousetrap.bind('g i', -> ShortcutsNavigation.findAndollowLink('.shortcuts-issues'))
|
||||
Mousetrap.bind('g m', -> ShortcutsNavigation.findAndollowLink('.shortcuts-merge_requests'))
|
||||
Mousetrap.bind('g w', -> ShortcutsNavigation.findAndollowLink('.shortcuts-wiki'))
|
||||
Mousetrap.bind('g s', -> ShortcutsNavigation.findAndollowLink('.shortcuts-snippets'))
|
||||
@enabledHelp.push('.hidden-shortcut.project')
|
||||
|
||||
@findAndollowLink: (selector) ->
|
||||
link = $(selector).attr('href')
|
||||
if link
|
||||
window.location = link
|
||||
@@ -0,0 +1,12 @@
|
||||
#= require shortcuts_navigation
|
||||
|
||||
class @ShortcutsNetwork extends ShortcutsNavigation
|
||||
constructor: (@graph) ->
|
||||
super()
|
||||
Mousetrap.bind(['left', 'h'], @graph.scrollLeft)
|
||||
Mousetrap.bind(['right', 'l'], @graph.scrollRight)
|
||||
Mousetrap.bind(['up', 'k'], @graph.scrollUp)
|
||||
Mousetrap.bind(['down', 'j'], @graph.scrollDown)
|
||||
Mousetrap.bind(['shift+up', 'shift+k'], @graph.scrollTop)
|
||||
Mousetrap.bind(['shift+down', 'shift+j'], @graph.scrollBottom)
|
||||
@enabledHelp.push('.hidden-shortcut.network')
|
||||
@@ -24,22 +24,7 @@ class window.ContributorsStatGraph
|
||||
class: 'graph-author-commits-count'
|
||||
})
|
||||
commits.text(author.commits + " commits")
|
||||
|
||||
additions = $('<span/>', {
|
||||
class: 'graph-additions'
|
||||
})
|
||||
additions.text(author.additions + " ++")
|
||||
|
||||
deletions = $('<span/>', {
|
||||
class: 'graph-deletions'
|
||||
})
|
||||
deletions.text(author.deletions + " --")
|
||||
|
||||
$('<span/>').append(commits)
|
||||
.append(" / ")
|
||||
.append(additions)
|
||||
.append(" / ")
|
||||
.append(deletions)
|
||||
|
||||
create_author_header: (author) ->
|
||||
list_item = $('<li/>', {
|
||||
|
||||
@@ -90,4 +90,4 @@ window.ContributorsStatGraphUtil =
|
||||
true
|
||||
else
|
||||
false
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
class @ZenMode
|
||||
@fullscreen_prefix = 'fullscreen_'
|
||||
|
||||
constructor: ->
|
||||
@active_zen_area = null
|
||||
@active_checkbox = null
|
||||
@scroll_position = 0
|
||||
|
||||
$(window).scroll =>
|
||||
if not @active_checkbox
|
||||
@scroll_position = window.pageYOffset
|
||||
|
||||
$('body').on 'change', '.zennable input[type=checkbox]', (e) =>
|
||||
checkbox = e.currentTarget
|
||||
if checkbox.checked
|
||||
# Disable other keyboard shortcuts in ZEN mode
|
||||
Mousetrap.pause()
|
||||
@udpateActiveZenArea(checkbox)
|
||||
else
|
||||
@exitZenMode()
|
||||
|
||||
$(document).on 'keydown', (e) =>
|
||||
if e.keyCode is $.ui.keyCode.ESCAPE
|
||||
@exitZenMode()
|
||||
e.preventDefault()
|
||||
|
||||
$(window).on 'hashchange', @updateZenModeFromLocationHash
|
||||
|
||||
udpateActiveZenArea: (checkbox) =>
|
||||
@active_checkbox = $(checkbox)
|
||||
@active_checkbox.prop('checked', true)
|
||||
@active_zen_area = @active_checkbox.parent().find('textarea')
|
||||
@active_zen_area.focus()
|
||||
window.location.hash = ZenMode.fullscreen_prefix + @active_checkbox.prop('id')
|
||||
|
||||
exitZenMode: =>
|
||||
if @active_zen_area isnt null
|
||||
Mousetrap.unpause()
|
||||
@active_checkbox.prop('checked', false)
|
||||
@active_zen_area = null
|
||||
@active_checkbox = null
|
||||
window.location.hash = ''
|
||||
window.scrollTo(window.pageXOffset, @scroll_position)
|
||||
|
||||
checkboxFromLocationHash: (e) ->
|
||||
id = $.trim(window.location.hash.replace('#' + ZenMode.fullscreen_prefix, ''))
|
||||
if id
|
||||
return $('.zennable input[type=checkbox]#' + id)[0]
|
||||
else
|
||||
return null
|
||||
|
||||
updateZenModeFromLocationHash: (e) =>
|
||||
checkbox = @checkboxFromLocationHash()
|
||||
if checkbox
|
||||
@udpateActiveZenArea(checkbox)
|
||||
else
|
||||
@exitZenMode()
|
||||
@@ -128,7 +128,7 @@ p.time {
|
||||
}
|
||||
|
||||
.highlight_word {
|
||||
border-bottom: 2px solid #F90;
|
||||
background: #fafe3d;
|
||||
}
|
||||
|
||||
.thin_area{
|
||||
|
||||
@@ -60,7 +60,6 @@
|
||||
|
||||
.highlight {
|
||||
margin-bottom: 9px;
|
||||
@include border-radius(4px);
|
||||
|
||||
> pre {
|
||||
margin: 0;
|
||||
|
||||
@@ -1,25 +1,17 @@
|
||||
.flash-container {
|
||||
display: none;
|
||||
cursor: pointer;
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
opacity: 0.8;
|
||||
z-index: 100;
|
||||
|
||||
.flash-notice {
|
||||
background: #49C;
|
||||
padding: 10px;
|
||||
text-shadow: 0 1px 1px #178;
|
||||
@extend .alert;
|
||||
@extend .alert-info;
|
||||
}
|
||||
|
||||
.flash-alert {
|
||||
background: #C67;
|
||||
text-shadow: 0 1px 1px #945;
|
||||
padding: 10px;
|
||||
@extend .alert;
|
||||
@extend .alert-danger;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
textarea {
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
input[type='search'].search-text-input {
|
||||
background-image: image-url("icon-search.png");
|
||||
background-repeat: no-repeat;
|
||||
@@ -83,3 +87,140 @@ label {
|
||||
.form-control {
|
||||
@include box-shadow(none);
|
||||
}
|
||||
|
||||
.issuable-description {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
/**
|
||||
* Styles that apply to both issues and merge requests.
|
||||
*/
|
||||
|
||||
.issue-form, .merge-request-form {
|
||||
.description {
|
||||
height: 20em;
|
||||
}
|
||||
}
|
||||
@@ -8,43 +8,51 @@
|
||||
*/
|
||||
|
||||
.issue-box {
|
||||
color: #666;
|
||||
color: #555;
|
||||
margin:20px 0;
|
||||
background: #FFF;
|
||||
border: 1px solid #EEE;
|
||||
@include box-shadow(0 1px 1px rgba(0, 0, 0, 0.05));
|
||||
background: #f9f9f9;
|
||||
border-top-left-radius: 5px;
|
||||
@include box-shadow(0 1px 1px rgba(0, 0, 0, 0.09));
|
||||
|
||||
&.issue-box-closed {
|
||||
border-color: $border_danger;
|
||||
.state {
|
||||
background-color: #F3CECE;
|
||||
border-color: $border_danger;
|
||||
}
|
||||
.state-label {
|
||||
background-color: $bg_danger;
|
||||
color: #FFF;
|
||||
border-color: $border_danger;
|
||||
}
|
||||
}
|
||||
|
||||
&.issue-box-merged {
|
||||
border-color: $border_primary;
|
||||
.state {
|
||||
background-color: #B7CEE7;
|
||||
border-color: $border_primary;
|
||||
}
|
||||
.state-label {
|
||||
background-color: $bg_primary;
|
||||
color: #FFF;
|
||||
border-color: $border_primary;
|
||||
}
|
||||
}
|
||||
|
||||
&.issue-box-open {
|
||||
border-color: $border_success;
|
||||
.state {
|
||||
border-color: $border_success;
|
||||
background-color: #D6F1D7;
|
||||
border-color: $bg_success;
|
||||
}
|
||||
.state-label {
|
||||
background-color: $bg_success;
|
||||
color: #FFF;
|
||||
}
|
||||
}
|
||||
|
||||
&.issue-box-expired {
|
||||
border-color: #cea61b;
|
||||
.state {
|
||||
background-color: #EEE9B3;
|
||||
border-color: #faebcc;
|
||||
}
|
||||
.state-label {
|
||||
background: #cea61b;
|
||||
color: #FFF;
|
||||
}
|
||||
@@ -55,8 +63,7 @@
|
||||
}
|
||||
|
||||
.state {
|
||||
border-bottom: 1px solid #DDD;
|
||||
padding: 10px 15px;
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.title {
|
||||
@@ -88,6 +95,10 @@
|
||||
|
||||
.description {
|
||||
padding: 0 15px 10px 15px;
|
||||
|
||||
code {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
|
||||
.title, .context, .description {
|
||||
@@ -100,12 +111,14 @@
|
||||
font-size: 14px;
|
||||
float: left;
|
||||
font-weight: bold;
|
||||
padding: 10px 15px;
|
||||
border-top-left-radius: 5px;
|
||||
}
|
||||
|
||||
.creator {
|
||||
float: right;
|
||||
padding: 10px 15px;
|
||||
a {
|
||||
color: #FFF;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
|
||||
&:hover {
|
||||
background: $hover;
|
||||
border-bottom: 1px solid #ADF;
|
||||
border-bottom: 1px solid darken($hover, 10%);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
.timeline {
|
||||
list-style: none;
|
||||
padding: 20px 0 20px;
|
||||
position: relative;
|
||||
|
||||
&:before {
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
position: absolute;
|
||||
content: " ";
|
||||
width: 3px;
|
||||
background-color: #eeeeee;
|
||||
margin-left: 29px;
|
||||
}
|
||||
|
||||
.timeline-entry {
|
||||
position: relative;
|
||||
margin-top: 5px;
|
||||
margin-left: 30px;
|
||||
margin-bottom: 10px;
|
||||
clear: both;
|
||||
|
||||
|
||||
&:target {
|
||||
.timeline-entry-inner .timeline-content {
|
||||
-webkit-animation:target-note 2s linear;
|
||||
background: $hover;
|
||||
}
|
||||
}
|
||||
|
||||
.timeline-entry-inner {
|
||||
position: relative;
|
||||
margin-left: -20px;
|
||||
|
||||
&:before, &:after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
|
||||
.timeline-icon {
|
||||
margin-top: 2px;
|
||||
background: #fff;
|
||||
color: #737881;
|
||||
float: left;
|
||||
@include border-radius(40px);
|
||||
@include box-shadow(0 0 0 3px #EEE);
|
||||
overflow: hidden;
|
||||
|
||||
.avatar {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.timeline-content {
|
||||
position: relative;
|
||||
background: #f5f5f6;
|
||||
padding: 10px 15px;
|
||||
margin-left: 60px;
|
||||
|
||||
&:after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-style: solid;
|
||||
border-width: 9px 9px 9px 0;
|
||||
border-color: transparent #f5f5f6 transparent transparent;
|
||||
left: 0;
|
||||
top: 10px;
|
||||
margin-left: -9px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ a {
|
||||
outline: none;
|
||||
color: $link_color;
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
text-decoration: underline;
|
||||
color: $link_hover_color;
|
||||
}
|
||||
|
||||
@@ -89,6 +89,8 @@ a:focus {
|
||||
.wiki {
|
||||
@include md-typography;
|
||||
|
||||
word-wrap: break-word;
|
||||
|
||||
/* Link to current header. */
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
position: relative;
|
||||
|
||||
@@ -1,14 +1,8 @@
|
||||
.white {
|
||||
background-color: #fff;
|
||||
|
||||
.line.hll {
|
||||
background: #FFA;
|
||||
}
|
||||
|
||||
.highlight{
|
||||
border-left: 1px solid #eee;
|
||||
}
|
||||
|
||||
pre {
|
||||
background-color: #fff;
|
||||
color: #333;
|
||||
@@ -179,8 +173,16 @@
|
||||
@include box-shadow(0 5px 15px #000);
|
||||
}
|
||||
|
||||
.wiki, .note-body {
|
||||
.highlight {
|
||||
border: 1px solid #DDD;
|
||||
.file-content {
|
||||
&.code .white {
|
||||
.highlight {
|
||||
border-left: 1px solid #eee;
|
||||
}
|
||||
}
|
||||
|
||||
&.wiki .white {
|
||||
.highlight, pre, .hljs {
|
||||
background: #F9F9F9;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,3 +16,4 @@ body {
|
||||
.container .content {
|
||||
margin: 0 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
* General Colors
|
||||
*/
|
||||
$style_color: #474D57;
|
||||
$hover: #D9EDF7;
|
||||
$hover: #FFECDB;
|
||||
|
||||
/*
|
||||
* Link colors
|
||||
*/
|
||||
$link_color: #446e9b;
|
||||
$link_hover_color: #2FA0BB;
|
||||
$link_hover_color: darken($link-color, 10%);
|
||||
|
||||
$btn-border: 1px solid #ccc;
|
||||
|
||||
|
||||
@@ -244,7 +244,6 @@ li.commit {
|
||||
font-family: inherit;
|
||||
padding-left: $left;
|
||||
position: relative;
|
||||
resize: vertical;
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,12 +60,13 @@
|
||||
}
|
||||
|
||||
.project-row, .group-row {
|
||||
padding: 8px 15px !important;
|
||||
padding: 0 !important;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
|
||||
a {
|
||||
display: block;
|
||||
padding: 8px 15px;
|
||||
}
|
||||
|
||||
.project-name, .group-name {
|
||||
@@ -99,14 +100,9 @@
|
||||
margin-right: 15px;
|
||||
font-size: 20px;
|
||||
margin-bottom: 15px;
|
||||
border: 1px solid #EEE;
|
||||
padding: 8px 12px;
|
||||
border-radius: 50px;
|
||||
background: #f5f5f5;
|
||||
text-align: center;
|
||||
|
||||
i {
|
||||
color: #BBB;
|
||||
color: #888;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -125,8 +125,6 @@
|
||||
}
|
||||
.line_content {
|
||||
display: block;
|
||||
white-space: pre;
|
||||
height: 18px;
|
||||
margin: 0px;
|
||||
padding: 0px 0.5em;
|
||||
border: none;
|
||||
@@ -341,3 +339,12 @@
|
||||
margin: 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.diff-file .line_content {
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.diff-wrap-lines .line_content {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,3 +17,56 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.shortcut-mappings {
|
||||
font-size: 12px;
|
||||
color: #555;
|
||||
|
||||
tbody:first-child tr:first-child {
|
||||
padding-top: 0
|
||||
}
|
||||
|
||||
th {
|
||||
padding-top: 15px;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
color: #333;
|
||||
text-align: left
|
||||
}
|
||||
|
||||
td {
|
||||
padding-top: 3px;
|
||||
padding-bottom: 3px;
|
||||
vertical-align: top;
|
||||
line-height: 20px
|
||||
}
|
||||
|
||||
.shortcut {
|
||||
padding-right: 10px;
|
||||
color: #999;
|
||||
text-align: right;
|
||||
white-space: nowrap
|
||||
}
|
||||
|
||||
.key {
|
||||
@extend .label;
|
||||
@extend .label-inverse;
|
||||
font: 11px Consolas, "Liberation Mono", Menlo, Courier, monospace;
|
||||
padding: 3px 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
position: relative;
|
||||
overflow-y: auto;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
body.modal-open {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.modal .modal-dialog {
|
||||
width: 860px;
|
||||
}
|
||||
|
||||
@@ -35,39 +35,31 @@
|
||||
width: 1%;
|
||||
&.active {
|
||||
a {
|
||||
color: #333;
|
||||
color: $link_color;
|
||||
font-weight: bold;
|
||||
&:after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: relative;
|
||||
bottom: 8px;
|
||||
left: 50%;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-color: transparent transparent #333 transparent;
|
||||
bottom: -1px;
|
||||
border-color: $link_color;
|
||||
border-style: solid;
|
||||
border-width: 6px;
|
||||
margin-left: -6px;
|
||||
border-width: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
a {
|
||||
color: $link_color;
|
||||
color: $link_hover_color;
|
||||
&:after {
|
||||
content: '';
|
||||
display: block;
|
||||
position: relative;
|
||||
bottom: 8px;
|
||||
left: 50%;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-color: transparent transparent $link_color transparent;
|
||||
bottom: -1px;
|
||||
border-color: $link_hover_color;
|
||||
border-style: solid;
|
||||
border-width: 6px;
|
||||
margin-left: -6px;
|
||||
border-width: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -90,7 +82,6 @@
|
||||
line-height: 34px;
|
||||
color: #777;
|
||||
text-shadow: 0 1px 1px white;
|
||||
padding: 0 10px;
|
||||
text-decoration: none;
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
@@ -17,9 +17,12 @@ ul.notes {
|
||||
.discussion-header,
|
||||
.note-header {
|
||||
@extend .cgray;
|
||||
padding-top: 5px;
|
||||
padding-bottom: 15px;
|
||||
|
||||
a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
float: left;
|
||||
margin-right: 10px;
|
||||
@@ -43,34 +46,19 @@ ul.notes {
|
||||
}
|
||||
|
||||
.discussion {
|
||||
padding: 10px 0;
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
position:relative;
|
||||
border-bottom: 1px solid #EEE;
|
||||
|
||||
.discussion-body {
|
||||
margin-left: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
.note {
|
||||
padding: 8px 0;
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
position:relative;
|
||||
border-bottom: 1px solid #eee;
|
||||
p { color: $style_color; }
|
||||
|
||||
.avatar {
|
||||
margin-top: 3px;
|
||||
}
|
||||
.attachment {
|
||||
font-size: 14px;
|
||||
}
|
||||
.note-body {
|
||||
@include md-typography;
|
||||
margin-left: 43px;
|
||||
}
|
||||
.note-header {
|
||||
padding-bottom: 3px;
|
||||
@@ -80,11 +68,6 @@ ul.notes {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.note:target {
|
||||
-webkit-animation:target-note 2s linear;
|
||||
background: #fffff0;
|
||||
}
|
||||
}
|
||||
|
||||
.diff-file .notes_holder {
|
||||
@@ -99,7 +82,7 @@ ul.notes {
|
||||
&.notes_line {
|
||||
text-align: center;
|
||||
padding: 10px 0;
|
||||
background: #eee;
|
||||
background: #FFF;
|
||||
}
|
||||
&.notes_line2 {
|
||||
text-align: center;
|
||||
@@ -111,6 +94,9 @@ ul.notes {
|
||||
border-width: 1px 0;
|
||||
padding-top: 0;
|
||||
vertical-align: top;
|
||||
&.parallel{
|
||||
border-width: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -161,8 +147,14 @@ ul.notes {
|
||||
*/
|
||||
|
||||
.diff-file tr.line_holder {
|
||||
@mixin show-add-diff-note {
|
||||
filter: alpha(opacity=100);
|
||||
opacity: 1.0;
|
||||
}
|
||||
|
||||
.add-diff-note {
|
||||
background: image-url("diff_note_add.png") no-repeat left 0;
|
||||
border: none;
|
||||
height: 22px;
|
||||
margin-left: -65px;
|
||||
position: absolute;
|
||||
@@ -174,8 +166,7 @@ ul.notes {
|
||||
filter: alpha(opacity=0);
|
||||
|
||||
&:hover {
|
||||
opacity: 1.0;
|
||||
filter: alpha(opacity=100);
|
||||
@include show-add-diff-note;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,8 +175,7 @@ ul.notes {
|
||||
background: $hover !important;
|
||||
|
||||
.add-diff-note {
|
||||
opacity: 1.0;
|
||||
filter: alpha(opacity=100);
|
||||
@include show-add-diff-note;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -362,3 +352,7 @@ ul.notes {
|
||||
border-top: 1px solid #DDD;
|
||||
}
|
||||
}
|
||||
|
||||
.discussion-notes-count {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
@@ -15,62 +15,68 @@
|
||||
}
|
||||
|
||||
.project-home-panel {
|
||||
border-bottom: 1px solid #DDD;
|
||||
padding-bottom: 15px;
|
||||
margin-bottom: 30px;
|
||||
margin-bottom: 15px;
|
||||
|
||||
&.empty-project {
|
||||
border-bottom: 0px;
|
||||
padding-bottom: 15px;
|
||||
margin-bottom: 0px;
|
||||
border-bottom: 0px;
|
||||
padding-bottom: 15px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.project-home-title {
|
||||
font-size: 18px;
|
||||
color: #444;
|
||||
margin: 0;
|
||||
line-height: 32px;
|
||||
}
|
||||
.project-home-dropdown {
|
||||
margin-left: 10px;
|
||||
float: right;
|
||||
}
|
||||
.project-home-extra {
|
||||
margin-top: 15px;
|
||||
|
||||
.project-home-row {
|
||||
@extend .clearfix;
|
||||
margin-bottom: 15px;
|
||||
|
||||
.project-home-desc {
|
||||
float: left;
|
||||
color: #777;
|
||||
margin-bottom: 10px;
|
||||
color: #666;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.project-home-links {
|
||||
.star-fork-buttons {
|
||||
float: right;
|
||||
a {
|
||||
margin-left: 10px;
|
||||
font-weight: 500;
|
||||
min-width: 200px;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
|
||||
.star-buttons, .fork-buttons {
|
||||
float: right;
|
||||
margin-left: 20px;
|
||||
|
||||
a:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.count {
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.visibility-level-label {
|
||||
font-size: 17px;
|
||||
background: #f1f1f1;
|
||||
border-radius: 4px;
|
||||
color: #444;
|
||||
position: absolute;
|
||||
margin-left: -55px;
|
||||
text-shadow: 0 1px 1px #FFF;
|
||||
width: 40px;
|
||||
text-align: center;
|
||||
padding: 6px;
|
||||
|
||||
color: #555;
|
||||
font-weight: bold;
|
||||
i {
|
||||
color: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.project-home-links {
|
||||
padding: 10px 0px;
|
||||
float: right;
|
||||
a {
|
||||
margin-left: 10px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.git-clone-holder {
|
||||
.project-home-dropdown + & {
|
||||
margin-right: 45px;
|
||||
@@ -159,6 +165,7 @@ ul.nav.nav-projects-tabs {
|
||||
li {
|
||||
.project-info {
|
||||
margin-bottom: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.access-icon {
|
||||
@@ -195,8 +202,8 @@ ul.nav.nav-projects-tabs {
|
||||
white-space: normal;
|
||||
text-align: left;
|
||||
padding: 10px 15px;
|
||||
background-color: #F1f1f1;
|
||||
border-color: #EEE;
|
||||
background-color: #F9F9F9;
|
||||
border-color: #DDD;
|
||||
|
||||
&:hover {
|
||||
background-color: #eee;
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
.search-results {
|
||||
.search-result-row {
|
||||
border-bottom: 1px solid #EEE;
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ class Admin::GroupsController < Admin::ApplicationController
|
||||
end
|
||||
|
||||
def show
|
||||
@members = @group.members.order("group_access DESC").page(params[:members_page]).per(30)
|
||||
@members = @group.members.order("access_level DESC").page(params[:members_page]).per(30)
|
||||
@projects = @group.projects.page(params[:projects_page]).per(30)
|
||||
end
|
||||
|
||||
@@ -40,7 +40,7 @@ class Admin::GroupsController < Admin::ApplicationController
|
||||
end
|
||||
|
||||
def project_teams_update
|
||||
@group.add_users(params[:user_ids].split(','), params[:group_access])
|
||||
@group.add_users(params[:user_ids].split(','), params[:access_level])
|
||||
|
||||
redirect_to [:admin, @group], notice: 'Users were successfully added.'
|
||||
end
|
||||
|
||||
@@ -16,10 +16,10 @@ class Admin::ProjectsController < Admin::ApplicationController
|
||||
|
||||
def show
|
||||
if @group
|
||||
@group_members = @group.members.order("group_access DESC").page(params[:group_members_page]).per(30)
|
||||
@group_members = @group.members.order("access_level DESC").page(params[:group_members_page]).per(30)
|
||||
end
|
||||
|
||||
@project_members = @project.users_projects.page(params[:project_members_page]).per(30)
|
||||
@project_members = @project.project_members.page(params[:project_members_page]).per(30)
|
||||
end
|
||||
|
||||
def transfer
|
||||
|
||||
+5
-5
@@ -1,4 +1,4 @@
|
||||
class UsersGroupsController < ApplicationController
|
||||
class Groups::GroupMembersController < ApplicationController
|
||||
before_filter :group
|
||||
|
||||
# Authorize
|
||||
@@ -7,18 +7,18 @@ class UsersGroupsController < ApplicationController
|
||||
layout 'group'
|
||||
|
||||
def create
|
||||
@group.add_users(params[:user_ids].split(','), params[:group_access])
|
||||
@group.add_users(params[:user_ids].split(','), params[:access_level])
|
||||
|
||||
redirect_to members_group_path(@group), notice: 'Users were successfully added.'
|
||||
end
|
||||
|
||||
def update
|
||||
@member = @group.users_groups.find(params[:id])
|
||||
@member = @group.group_members.find(params[:id])
|
||||
@member.update_attributes(member_params)
|
||||
end
|
||||
|
||||
def destroy
|
||||
@users_group = @group.users_groups.find(params[:id])
|
||||
@users_group = @group.group_members.find(params[:id])
|
||||
if can?(current_user, :destroy, @users_group) # May fail if last owner.
|
||||
@users_group.destroy
|
||||
respond_to do |format|
|
||||
@@ -43,6 +43,6 @@ class UsersGroupsController < ApplicationController
|
||||
end
|
||||
|
||||
def member_params
|
||||
params.require(:users_group).permit(:group_access, :user_id)
|
||||
params.require(:group_member).permit(:access_level, :user_id)
|
||||
end
|
||||
end
|
||||
@@ -65,15 +65,15 @@ class GroupsController < ApplicationController
|
||||
|
||||
def members
|
||||
@project = group.projects.find(params[:project_id]) if params[:project_id]
|
||||
@members = group.users_groups
|
||||
@members = group.group_members
|
||||
|
||||
if params[:search].present?
|
||||
users = group.users.search(params[:search]).to_a
|
||||
@members = @members.where(user_id: users)
|
||||
end
|
||||
|
||||
@members = @members.order('group_access DESC').page(params[:page]).per(50)
|
||||
@users_group = UsersGroup.new
|
||||
@members = @members.order('access_level DESC').page(params[:page]).per(50)
|
||||
@users_group = GroupMember.new
|
||||
end
|
||||
|
||||
def edit
|
||||
|
||||
@@ -2,11 +2,11 @@ class Profiles::GroupsController < ApplicationController
|
||||
layout "profile"
|
||||
|
||||
def index
|
||||
@user_groups = current_user.users_groups.page(params[:page]).per(20)
|
||||
@user_groups = current_user.group_members.page(params[:page]).per(20)
|
||||
end
|
||||
|
||||
def leave
|
||||
@users_group = group.users_groups.where(user_id: current_user.id).first
|
||||
@users_group = group.group_members.where(user_id: current_user.id).first
|
||||
if can?(current_user, :destroy, @users_group)
|
||||
@users_group.destroy
|
||||
redirect_to(profile_groups_path, info: "You left #{group.name} group.")
|
||||
|
||||
@@ -3,8 +3,8 @@ class Profiles::NotificationsController < ApplicationController
|
||||
|
||||
def show
|
||||
@notification = current_user.notification
|
||||
@users_projects = current_user.users_projects
|
||||
@users_groups = current_user.users_groups
|
||||
@project_members = current_user.project_members
|
||||
@group_members = current_user.group_members
|
||||
end
|
||||
|
||||
def update
|
||||
@@ -14,13 +14,13 @@ class Profiles::NotificationsController < ApplicationController
|
||||
current_user.notification_level = params[:notification_level]
|
||||
current_user.save
|
||||
elsif type == 'group'
|
||||
users_group = current_user.users_groups.find(params[:notification_id])
|
||||
users_group = current_user.group_members.find(params[:notification_id])
|
||||
users_group.notification_level = params[:notification_level]
|
||||
users_group.save
|
||||
else
|
||||
users_project = current_user.users_projects.find(params[:notification_id])
|
||||
users_project.notification_level = params[:notification_level]
|
||||
users_project.save
|
||||
project_member = current_user.project_members.find(params[:notification_id])
|
||||
project_member.notification_level = params[:notification_level]
|
||||
project_member.save
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -17,13 +17,19 @@ class Projects::BranchesController < Projects::ApplicationController
|
||||
end
|
||||
|
||||
def create
|
||||
@branch = CreateBranchService.new.execute(project, params[:branch_name], params[:ref], current_user)
|
||||
|
||||
redirect_to project_tree_path(@project, @branch.name)
|
||||
result = CreateBranchService.new(project, current_user).
|
||||
execute(params[:branch_name], params[:ref])
|
||||
if result[:status] == :success
|
||||
@branch = result[:branch]
|
||||
redirect_to project_tree_path(@project, @branch.name)
|
||||
else
|
||||
@error = result[:message]
|
||||
render action: 'new'
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
DeleteBranchService.new.execute(project, params[:id], current_user)
|
||||
DeleteBranchService.new(project, current_user).execute(params[:id])
|
||||
@branch_name = params[:id]
|
||||
|
||||
respond_to do |format|
|
||||
|
||||
@@ -19,13 +19,7 @@ class Projects::CommitController < Projects::ApplicationController
|
||||
[]
|
||||
end
|
||||
|
||||
begin
|
||||
@diffs = @commit.diffs
|
||||
rescue Grit::Git::GitTimeout
|
||||
@diffs = []
|
||||
@diff_timeout = true
|
||||
end
|
||||
|
||||
@diffs = @commit.diffs
|
||||
@note = project.build_commit_note(commit)
|
||||
@notes_count = project.notes.for_commit_id(commit.id).count
|
||||
@notes = project.notes.for_commit_id(@commit.id).not_inline.fresh
|
||||
|
||||
@@ -10,7 +10,8 @@ class Projects::EditTreeController < Projects::BaseTreeController
|
||||
end
|
||||
|
||||
def update
|
||||
result = Files::UpdateService.new(@project, current_user, params, @ref, @path).execute
|
||||
result = Files::UpdateService.
|
||||
new(@project, current_user, params, @ref, @path).execute
|
||||
|
||||
if result[:status] == :success
|
||||
flash[:notice] = "Your changes have been successfully committed"
|
||||
@@ -31,7 +32,7 @@ class Projects::EditTreeController < Projects::BaseTreeController
|
||||
|
||||
diffy = Diffy::Diff.new(@blob.data, @content, diff: '-U 3',
|
||||
include_diff_info: true)
|
||||
@diff = Gitlab::DiffParser.new(diffy.diff.scan(/.*\n/))
|
||||
@diff_lines = Gitlab::Diff::Parser.new.parse(diffy.diff.scan(/.*\n/))
|
||||
|
||||
render layout: false
|
||||
end
|
||||
|
||||
@@ -7,19 +7,34 @@ class Projects::GraphsController < Projects::ApplicationController
|
||||
def show
|
||||
respond_to do |format|
|
||||
format.html
|
||||
format.js do
|
||||
format.json do
|
||||
fetch_graph
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def commits
|
||||
@commits = @project.repository.commits(nil, nil, 2000, 0, true)
|
||||
@commits_graph = Gitlab::Graphs::Commits.new(@commits)
|
||||
@commits_per_week_days = @commits_graph.commits_per_week_days
|
||||
@commits_per_time = @commits_graph.commits_per_time
|
||||
@commits_per_month = @commits_graph.commits_per_month
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def fetch_graph
|
||||
@log = @project.repository.graph_log.to_json
|
||||
@success = true
|
||||
rescue => ex
|
||||
@commits = @project.repository.commits(nil, nil, 6000, 0, true)
|
||||
@log = []
|
||||
@success = false
|
||||
|
||||
@commits.each do |commit|
|
||||
@log << {
|
||||
author_name: commit.author_name.force_encoding('UTF-8'),
|
||||
author_email: commit.author_email.force_encoding('UTF-8'),
|
||||
date: commit.committed_date.strftime("%Y-%m-%d")
|
||||
}
|
||||
end
|
||||
|
||||
render json: @log.to_json
|
||||
end
|
||||
end
|
||||
|
||||
@@ -20,7 +20,7 @@ class Projects::IssuesController < Projects::ApplicationController
|
||||
terms = params['issue_search']
|
||||
|
||||
@issues = issues_filtered
|
||||
@issues = @issues.where("title LIKE ? OR description LIKE ?", "%#{terms}%", "%#{terms}%") if terms.present?
|
||||
@issues = @issues.full_search(terms) if terms.present?
|
||||
@issues = @issues.page(params[:page]).per(20)
|
||||
|
||||
assignee_id, milestone_id = params[:assignee_id], params[:milestone_id]
|
||||
@@ -28,7 +28,7 @@ class Projects::IssuesController < Projects::ApplicationController
|
||||
@milestone = @project.milestones.find(milestone_id) if milestone_id.present? && !milestone_id.to_i.zero?
|
||||
sort_param = params[:sort] || 'newest'
|
||||
@sort = sort_param.humanize unless sort_param.empty?
|
||||
@assignees = User.where(id: @project.issues.pluck(:assignee_id))
|
||||
@assignees = User.where(id: @project.issues.pluck(:assignee_id)).active
|
||||
|
||||
respond_to do |format|
|
||||
format.html
|
||||
|
||||
@@ -52,7 +52,7 @@ class Projects::LabelsController < Projects::ApplicationController
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to project_labels_path(@project), notice: 'Label was removed' }
|
||||
format.js { render nothing: true }
|
||||
format.js
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ class Projects::NewTreeController < Projects::BaseTreeController
|
||||
flash[:notice] = "Your changes have been successfully committed"
|
||||
redirect_to project_blob_path(@project, File.join(@ref, file_path))
|
||||
else
|
||||
flash[:alert] = result[:error]
|
||||
flash[:alert] = result[:message]
|
||||
render :show
|
||||
end
|
||||
end
|
||||
|
||||
@@ -30,8 +30,10 @@ class Projects::NotesController < Projects::ApplicationController
|
||||
end
|
||||
|
||||
def update
|
||||
note.update_attributes(note_params)
|
||||
note.reset_events_cache
|
||||
if note.editable?
|
||||
note.update_attributes(note_params)
|
||||
note.reset_events_cache
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.json { render_note_json(note) }
|
||||
@@ -40,8 +42,10 @@ class Projects::NotesController < Projects::ApplicationController
|
||||
end
|
||||
|
||||
def destroy
|
||||
note.destroy
|
||||
note.reset_events_cache
|
||||
if note.editable?
|
||||
note.destroy
|
||||
note.reset_events_cache
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.js { render nothing: true }
|
||||
|
||||
@@ -29,12 +29,10 @@ class Projects::RawController < Projects::ApplicationController
|
||||
private
|
||||
|
||||
def get_blob_type
|
||||
if @blob.mime_type =~ /html|javascript/
|
||||
if @blob.text?
|
||||
'text/plain; charset=utf-8'
|
||||
elsif @blob.name =~ /(?:msi|exe|rar|r0\d|7z|7zip|zip)$/
|
||||
'application/octet-stream'
|
||||
else
|
||||
@blob.mime_type
|
||||
'application/octet-stream'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,11 +4,6 @@ class Projects::RepositoriesController < Projects::ApplicationController
|
||||
before_filter :authorize_code_access!
|
||||
before_filter :require_non_empty_project
|
||||
|
||||
def stats
|
||||
@stats = Gitlab::Git::Stats.new(@repository.raw, @repository.root_ref)
|
||||
@graph = @stats.graph
|
||||
end
|
||||
|
||||
def archive
|
||||
unless can?(current_user, :download_code, @project)
|
||||
render_404 and return
|
||||
|
||||
@@ -13,10 +13,15 @@ class Projects::TagsController < Projects::ApplicationController
|
||||
end
|
||||
|
||||
def create
|
||||
@tag = CreateTagService.new.execute(@project, params[:tag_name],
|
||||
params[:ref], current_user)
|
||||
|
||||
redirect_to project_tags_path(@project)
|
||||
result = CreateTagService.new(@project, current_user).
|
||||
execute(params[:tag_name], params[:ref], params[:message])
|
||||
if result[:status] == :success
|
||||
@tag = result[:tag]
|
||||
redirect_to project_tags_path(@project)
|
||||
else
|
||||
@error = result[:message]
|
||||
render action: 'new'
|
||||
end
|
||||
end
|
||||
|
||||
def destroy
|
||||
@@ -28,7 +33,7 @@ class Projects::TagsController < Projects::ApplicationController
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to project_tags_path }
|
||||
format.js { render nothing: true }
|
||||
format.js
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,17 +6,17 @@ class Projects::TeamMembersController < Projects::ApplicationController
|
||||
|
||||
def index
|
||||
@group = @project.group
|
||||
@users_projects = @project.users_projects.order('project_access DESC')
|
||||
@project_members = @project.project_members.order('access_level DESC')
|
||||
end
|
||||
|
||||
def new
|
||||
@user_project_relation = project.users_projects.new
|
||||
@user_project_relation = project.project_members.new
|
||||
end
|
||||
|
||||
def create
|
||||
users = User.where(id: params[:user_ids].split(','))
|
||||
|
||||
@project.team << [users, params[:project_access]]
|
||||
@project.team << [users, params[:access_level]]
|
||||
|
||||
if params[:redirect_to]
|
||||
redirect_to params[:redirect_to]
|
||||
@@ -26,7 +26,7 @@ class Projects::TeamMembersController < Projects::ApplicationController
|
||||
end
|
||||
|
||||
def update
|
||||
@user_project_relation = project.users_projects.find_by(user_id: member)
|
||||
@user_project_relation = project.project_members.find_by(user_id: member)
|
||||
@user_project_relation.update_attributes(member_params)
|
||||
|
||||
unless @user_project_relation.valid?
|
||||
@@ -36,7 +36,7 @@ class Projects::TeamMembersController < Projects::ApplicationController
|
||||
end
|
||||
|
||||
def destroy
|
||||
@user_project_relation = project.users_projects.find_by(user_id: member)
|
||||
@user_project_relation = project.project_members.find_by(user_id: member)
|
||||
@user_project_relation.destroy
|
||||
|
||||
respond_to do |format|
|
||||
@@ -46,7 +46,7 @@ class Projects::TeamMembersController < Projects::ApplicationController
|
||||
end
|
||||
|
||||
def leave
|
||||
project.users_projects.find_by(user_id: current_user).destroy
|
||||
project.project_members.find_by(user_id: current_user).destroy
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to :back }
|
||||
@@ -69,6 +69,6 @@ class Projects::TeamMembersController < Projects::ApplicationController
|
||||
end
|
||||
|
||||
def member_params
|
||||
params.require(:team_member).permit(:user_id, :project_access)
|
||||
params.require(:project_member).permit(:user_id, :access_level)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -103,7 +103,15 @@ class ProjectsController < ApplicationController
|
||||
::Projects::DestroyService.new(@project, current_user, {}).execute
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to root_path }
|
||||
format.html do
|
||||
flash[:alert] = "Project deleted."
|
||||
|
||||
if request.referer.include?("/admin")
|
||||
redirect_to admin_projects_path
|
||||
else
|
||||
redirect_to projects_dashboard_path
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -4,14 +4,33 @@ class SearchController < ApplicationController
|
||||
def show
|
||||
@project = Project.find_by(id: params[:project_id]) if params[:project_id].present?
|
||||
@group = Group.find_by(id: params[:group_id]) if params[:group_id].present?
|
||||
@scope = params[:scope]
|
||||
@show_snippets = params[:snippets].eql? 'true'
|
||||
|
||||
if @project
|
||||
return access_denied! unless can?(current_user, :download_code, @project)
|
||||
@search_results = if @project
|
||||
return access_denied! unless can?(current_user, :download_code, @project)
|
||||
|
||||
@search_results = Search::ProjectService.new(@project, current_user, params).execute
|
||||
else
|
||||
@search_results = Search::GlobalService.new(current_user, params).execute
|
||||
end
|
||||
unless %w(blobs notes issues merge_requests wiki_blobs).
|
||||
include?(@scope)
|
||||
@scope = 'blobs'
|
||||
end
|
||||
|
||||
Search::ProjectService.new(@project, current_user, params).execute
|
||||
elsif @show_snippets
|
||||
unless %w(snippet_blobs snippet_titles).include?(@scope)
|
||||
@scope = 'snippet_blobs'
|
||||
end
|
||||
|
||||
Search::SnippetService.new(current_user, params).execute
|
||||
else
|
||||
unless %w(projects issues merge_requests).include?(@scope)
|
||||
@scope = 'projects'
|
||||
end
|
||||
|
||||
Search::GlobalService.new(current_user, params).execute
|
||||
end
|
||||
|
||||
@objects = @search_results.objects(@scope, params[:page])
|
||||
end
|
||||
|
||||
def autocomplete
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# BaseFinder
|
||||
# IssuableFinder
|
||||
#
|
||||
# Used to filter Issues and MergeRequests collections by set of params
|
||||
#
|
||||
@@ -16,7 +16,9 @@
|
||||
# label_name: string
|
||||
# sort: string
|
||||
#
|
||||
class BaseFinder
|
||||
require_relative 'projects_finder'
|
||||
|
||||
class IssuableFinder
|
||||
attr_accessor :current_user, :params
|
||||
|
||||
def execute(current_user, params)
|
||||
@@ -15,7 +15,7 @@
|
||||
# label_name: string
|
||||
# sort: string
|
||||
#
|
||||
class IssuesFinder < BaseFinder
|
||||
class IssuesFinder < IssuableFinder
|
||||
def klass
|
||||
Issue
|
||||
end
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
# label_name: string
|
||||
# sort: string
|
||||
#
|
||||
class MergeRequestsFinder < BaseFinder
|
||||
class MergeRequestsFinder < IssuableFinder
|
||||
def klass
|
||||
MergeRequest
|
||||
end
|
||||
|
||||
@@ -19,10 +19,8 @@ class ProjectsFinder
|
||||
# Return ALL group projects
|
||||
group.projects
|
||||
else
|
||||
projects_members = UsersProject.where(
|
||||
project_id: group.projects,
|
||||
user_id: current_user
|
||||
)
|
||||
projects_members = ProjectMember.in_projects(group.projects).
|
||||
with_user(current_user)
|
||||
|
||||
if projects_members.any?
|
||||
# User is a project member
|
||||
@@ -34,7 +32,7 @@ class ProjectsFinder
|
||||
#
|
||||
group.projects.where(
|
||||
"projects.id IN (?) OR projects.visibility_level IN (?)",
|
||||
projects_members.pluck(:project_id),
|
||||
projects_members.pluck(:source_id),
|
||||
Project.public_and_internal_levels
|
||||
)
|
||||
else
|
||||
|
||||
@@ -178,6 +178,8 @@ module ApplicationHelper
|
||||
def search_placeholder
|
||||
if @project && @project.persisted?
|
||||
"Search in this project"
|
||||
elsif @snippet || @snippets || @show_snippets
|
||||
'Search snippets'
|
||||
elsif @group && @group.persisted?
|
||||
"Search in this group"
|
||||
else
|
||||
@@ -185,13 +187,6 @@ module ApplicationHelper
|
||||
end
|
||||
end
|
||||
|
||||
def first_line(str)
|
||||
lines = str.split("\n")
|
||||
line = lines.first
|
||||
line += "..." if lines.size > 1
|
||||
line
|
||||
end
|
||||
|
||||
def broadcast_message
|
||||
BroadcastMessage.current
|
||||
end
|
||||
|
||||
@@ -16,38 +16,6 @@ module CommitsHelper
|
||||
commit_person_link(commit, options.merge(source: :committer))
|
||||
end
|
||||
|
||||
def each_diff_line(diff, index)
|
||||
Gitlab::DiffParser.new(diff.diff.lines.to_a, diff.new_path)
|
||||
.each do |full_line, type, line_code, line_new, line_old|
|
||||
yield(full_line, type, line_code, line_new, line_old)
|
||||
end
|
||||
end
|
||||
|
||||
def each_diff_line_near(diff, index, expected_line_code)
|
||||
max_number_of_lines = 16
|
||||
|
||||
prev_match_line = nil
|
||||
prev_lines = []
|
||||
|
||||
each_diff_line(diff, index) do |full_line, type, line_code, line_new, line_old|
|
||||
line = [full_line, type, line_code, line_new, line_old]
|
||||
if line_code != expected_line_code
|
||||
if type == "match"
|
||||
prev_lines.clear
|
||||
prev_match_line = line
|
||||
else
|
||||
prev_lines.push(line)
|
||||
prev_lines.shift if prev_lines.length >= max_number_of_lines
|
||||
end
|
||||
else
|
||||
yield(prev_match_line) if !prev_match_line.nil?
|
||||
prev_lines.each { |ln| yield(ln) }
|
||||
yield(line)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def image_diff_class(diff)
|
||||
if diff.deleted_file
|
||||
"deleted"
|
||||
@@ -63,14 +31,6 @@ module CommitsHelper
|
||||
escape_javascript(render "projects/commits/#{template}", commit: commit, project: project) unless commit.nil?
|
||||
end
|
||||
|
||||
def diff_line_content(line)
|
||||
if line.blank?
|
||||
" "
|
||||
else
|
||||
line
|
||||
end
|
||||
end
|
||||
|
||||
# Breadcrumb links for a Project and, if applicable, a tree path
|
||||
def commits_breadcrumbs
|
||||
return unless @project && @ref
|
||||
@@ -105,82 +65,6 @@ module CommitsHelper
|
||||
branches.sort.map { |branch| link_to(branch, project_tree_path(project, branch)) }.join(", ").html_safe
|
||||
end
|
||||
|
||||
def parallel_diff_lines(project, commit, diff, file)
|
||||
old_file = project.repository.blob_at(commit.parent_id, diff.old_path) if commit.parent_id
|
||||
deleted_lines = {}
|
||||
added_lines = {}
|
||||
each_diff_line(diff, 0) do |line, type, line_code, line_new, line_old|
|
||||
if type == "old"
|
||||
deleted_lines[line_old] = { line_code: line_code, type: type, line: line }
|
||||
elsif type == "new"
|
||||
added_lines[line_new] = { line_code: line_code, type: type, line: line }
|
||||
end
|
||||
end
|
||||
max_length = old_file ? [old_file.loc, file.loc].max : file.loc
|
||||
|
||||
offset1 = 0
|
||||
offset2 = 0
|
||||
old_lines = []
|
||||
new_lines = []
|
||||
|
||||
max_length.times do |line_index|
|
||||
line_index1 = line_index - offset1
|
||||
line_index2 = line_index - offset2
|
||||
deleted_line = deleted_lines[line_index1 + 1]
|
||||
added_line = added_lines[line_index2 + 1]
|
||||
old_line = old_file.lines[line_index1] if old_file
|
||||
new_line = file.lines[line_index2]
|
||||
|
||||
if deleted_line && added_line
|
||||
elsif deleted_line
|
||||
new_line = nil
|
||||
offset2 += 1
|
||||
elsif added_line
|
||||
old_line = nil
|
||||
offset1 += 1
|
||||
end
|
||||
|
||||
old_lines[line_index] = DiffLine.new
|
||||
new_lines[line_index] = DiffLine.new
|
||||
|
||||
# old
|
||||
if line_index == 0 && diff.new_file
|
||||
old_lines[line_index].type = :file_created
|
||||
old_lines[line_index].content = 'File was created'
|
||||
elsif deleted_line
|
||||
old_lines[line_index].type = :deleted
|
||||
old_lines[line_index].content = old_line
|
||||
old_lines[line_index].num = line_index1 + 1
|
||||
old_lines[line_index].code = deleted_line[:line_code]
|
||||
elsif old_line
|
||||
old_lines[line_index].type = :no_change
|
||||
old_lines[line_index].content = old_line
|
||||
old_lines[line_index].num = line_index1 + 1
|
||||
else
|
||||
old_lines[line_index].type = :added
|
||||
end
|
||||
|
||||
# new
|
||||
if line_index == 0 && diff.deleted_file
|
||||
new_lines[line_index].type = :file_deleted
|
||||
new_lines[line_index].content = "File was deleted"
|
||||
elsif added_line
|
||||
new_lines[line_index].type = :added
|
||||
new_lines[line_index].num = line_index2 + 1
|
||||
new_lines[line_index].content = new_line
|
||||
new_lines[line_index].code = added_line[:line_code]
|
||||
elsif new_line
|
||||
new_lines[line_index].type = :no_change
|
||||
new_lines[line_index].num = line_index2 + 1
|
||||
new_lines[line_index].content = new_line
|
||||
else
|
||||
new_lines[line_index].type = :deleted
|
||||
end
|
||||
end
|
||||
|
||||
return old_lines, new_lines
|
||||
end
|
||||
|
||||
def link_to_browse_code(project, commit)
|
||||
if current_controller?(:projects, :commits)
|
||||
if @repo.blob_at(commit.id, @path)
|
||||
@@ -229,14 +113,6 @@ module CommitsHelper
|
||||
end
|
||||
end
|
||||
|
||||
def diff_file_mode_changed?(diff)
|
||||
diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode
|
||||
end
|
||||
|
||||
def unfold_bottom_class(bottom)
|
||||
(bottom) ? 'js-unfold-bottom' : ''
|
||||
end
|
||||
|
||||
def view_file_btn(commit_sha, diff, project)
|
||||
link_to project_blob_path(project, tree_join(commit_sha, diff.new_path)),
|
||||
class: 'btn btn-small view-file js-view-file' do
|
||||
|
||||
+103
-5
@@ -1,14 +1,20 @@
|
||||
module DiffHelper
|
||||
def safe_diff_files(diffs)
|
||||
def allowed_diff_size
|
||||
if diff_hard_limit_enabled?
|
||||
diffs.first(Commit::DIFF_HARD_LIMIT_FILES)
|
||||
Commit::DIFF_HARD_LIMIT_FILES
|
||||
else
|
||||
diffs.first(Commit::DIFF_SAFE_FILES)
|
||||
Commit::DIFF_SAFE_FILES
|
||||
end
|
||||
end
|
||||
|
||||
def show_diff_size_warninig?(diffs)
|
||||
safe_diff_files(diffs).size < diffs.size
|
||||
def safe_diff_files(diffs)
|
||||
diffs.first(allowed_diff_size).map do |diff|
|
||||
Gitlab::Diff::File.new(diff)
|
||||
end
|
||||
end
|
||||
|
||||
def show_diff_size_warning?(diffs)
|
||||
diffs.size > allowed_diff_size
|
||||
end
|
||||
|
||||
def diff_hard_limit_enabled?
|
||||
@@ -19,4 +25,96 @@ module DiffHelper
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def generate_line_code(file_path, line)
|
||||
Gitlab::Diff::LineCode.generate(file_path, line.new_pos, line.old_pos)
|
||||
end
|
||||
|
||||
def parallel_diff(diff_file, index)
|
||||
lines = []
|
||||
skip_next = false
|
||||
|
||||
# Building array of lines
|
||||
#
|
||||
# [
|
||||
# left_type, left_line_number, left_line_content, left_line_code,
|
||||
# right_line_type, right_line_number, right_line_content, right_line_code
|
||||
# ]
|
||||
#
|
||||
diff_file.diff_lines.each do |line|
|
||||
|
||||
full_line = line.text
|
||||
type = line.type
|
||||
line_code = generate_line_code(diff_file.file_path, line)
|
||||
line_new = line.new_pos
|
||||
line_old = line.old_pos
|
||||
|
||||
next_line = diff_file.next_line(line.index)
|
||||
|
||||
if next_line
|
||||
next_line_code = generate_line_code(diff_file.file_path, next_line)
|
||||
next_type = next_line.type
|
||||
next_line = next_line.text
|
||||
end
|
||||
|
||||
if type == 'match' || type.nil?
|
||||
# line in the right panel is the same as in the left one
|
||||
line = [type, line_old, full_line, line_code, type, line_new, full_line, line_code]
|
||||
lines.push(line)
|
||||
elsif type == 'old'
|
||||
if next_type == 'new'
|
||||
# Left side has text removed, right side has text added
|
||||
line = [type, line_old, full_line, line_code, next_type, line_new, next_line, next_line_code]
|
||||
lines.push(line)
|
||||
skip_next = true
|
||||
elsif next_type == 'old' || next_type.nil?
|
||||
# Left side has text removed, right side doesn't have any change
|
||||
# No next line code, no new line number, no new line text
|
||||
line = [type, line_old, full_line, line_code, next_type, nil, " ", nil]
|
||||
lines.push(line)
|
||||
end
|
||||
elsif type == 'new'
|
||||
if skip_next
|
||||
# Change has been already included in previous line so no need to do it again
|
||||
skip_next = false
|
||||
next
|
||||
else
|
||||
# Change is only on the right side, left side has no change
|
||||
line = [nil, nil, " ", line_code, type, line_new, full_line, line_code]
|
||||
lines.push(line)
|
||||
end
|
||||
end
|
||||
end
|
||||
lines
|
||||
end
|
||||
|
||||
def unfold_bottom_class(bottom)
|
||||
(bottom) ? 'js-unfold-bottom' : ''
|
||||
end
|
||||
|
||||
def diff_line_content(line)
|
||||
if line.blank?
|
||||
" "
|
||||
else
|
||||
line
|
||||
end
|
||||
end
|
||||
|
||||
def line_comments
|
||||
@line_comments ||= @line_notes.group_by(&:line_code)
|
||||
end
|
||||
|
||||
def organize_comments(type_left, type_right, line_code_left, line_code_right)
|
||||
comments_left = comments_right = nil
|
||||
|
||||
unless type_left.nil? && type_right == 'new'
|
||||
comments_left = line_comments[line_code_left]
|
||||
end
|
||||
|
||||
unless type_left.nil? && type_right.nil?
|
||||
comments_right = line_comments[line_code_right]
|
||||
end
|
||||
|
||||
[comments_left, comments_right]
|
||||
end
|
||||
end
|
||||
|
||||
@@ -52,6 +52,8 @@ module EventsHelper
|
||||
"#{event.author_name} #{event.push_action_name} #{event.ref_type} #{event.ref_name} at #{event.project_name}"
|
||||
elsif event.membership_changed?
|
||||
"#{event.author_name} #{event.action_name} #{event.project_name}"
|
||||
elsif event.note? && event.note_commit?
|
||||
"#{event.author_name} commented on #{event.note_target_type} #{event.note_short_commit_id} at #{event.project_name}"
|
||||
elsif event.note?
|
||||
"#{event.author_name} commented on #{event.note_target_type} ##{truncate event.note_target_iid} at #{event.project_name}"
|
||||
else
|
||||
@@ -64,6 +66,8 @@ module EventsHelper
|
||||
project_issue_url(event.project, event.issue)
|
||||
elsif event.merge_request?
|
||||
project_merge_request_url(event.project, event.merge_request)
|
||||
elsif event.note? && event.note_commit?
|
||||
project_commit_url(event.project, event.note_target)
|
||||
elsif event.note?
|
||||
if event.note_target
|
||||
if event.note_commit?
|
||||
@@ -94,6 +98,8 @@ module EventsHelper
|
||||
render "events/event_push", event: event
|
||||
elsif event.merge_request?
|
||||
render "events/event_merge_request", merge_request: event.merge_request
|
||||
elsif event.push?
|
||||
render "events/event_push", event: event
|
||||
elsif event.note?
|
||||
render "events/event_note", note: event.note
|
||||
end
|
||||
@@ -130,7 +136,7 @@ module EventsHelper
|
||||
end
|
||||
|
||||
def event_note(text)
|
||||
text = first_line(text)
|
||||
text = first_line_in_markdown(text)
|
||||
text = truncate(text, length: 150)
|
||||
sanitize(markdown(text), tags: %w(a img b pre p))
|
||||
end
|
||||
|
||||
@@ -51,6 +51,14 @@ module GitlabMarkdownHelper
|
||||
@markdown.render(text).html_safe
|
||||
end
|
||||
|
||||
def first_line_in_markdown(text)
|
||||
line = text.split("\n").detect do |i|
|
||||
i.present? && markdown(i).present?
|
||||
end
|
||||
line += '...' unless line.nil?
|
||||
line
|
||||
end
|
||||
|
||||
def render_wiki_content(wiki_page)
|
||||
if wiki_page.format == :markdown
|
||||
markdown(wiki_page.content)
|
||||
@@ -65,7 +73,12 @@ module GitlabMarkdownHelper
|
||||
paths.uniq.each do |file_path|
|
||||
# If project does not have repository
|
||||
# its nothing to rebuild
|
||||
if @repository.exists? && !@repository.empty?
|
||||
#
|
||||
# TODO: pass project variable to markdown helper instead of using
|
||||
# instance variable. Right now it generates invalid path for pages out
|
||||
# of project scope. Example: search results where can be rendered markdown
|
||||
# from different projects
|
||||
if @repository && @repository.exists? && !@repository.empty?
|
||||
new_path = rebuild_path(file_path)
|
||||
# Finds quoted path so we don't replace other mentions of the string
|
||||
# eg. "doc/api" will be replaced and "/home/doc/api/text" won't
|
||||
|
||||
@@ -52,8 +52,8 @@ module NotesHelper
|
||||
discussion_id: discussion_id
|
||||
}
|
||||
|
||||
link_to "", "javascript:;", class: "add-diff-note js-add-diff-note-button",
|
||||
data: data, title: "Add a comment to this line"
|
||||
button_tag '', class: 'btn add-diff-note js-add-diff-note-button',
|
||||
data: data, title: 'Add a comment to this line'
|
||||
end
|
||||
|
||||
def link_to_reply_diff(note)
|
||||
@@ -67,11 +67,10 @@ module NotesHelper
|
||||
discussion_id: note.discussion_id
|
||||
}
|
||||
|
||||
link_to "javascript:;", class: "btn reply-btn js-discussion-reply-button",
|
||||
data: data, title: "Add a reply" do
|
||||
link_text = ""
|
||||
link_text < content_tag(:i, nil, class: 'icon-comment')
|
||||
link_text << "Reply"
|
||||
end
|
||||
button_tag class: 'btn reply-btn js-discussion-reply-button',
|
||||
data: data, title: 'Add a reply' do
|
||||
link_text = content_tag(:i, nil, class: 'icon-comment')
|
||||
link_text << ' Reply'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -123,7 +123,7 @@ module ProjectsHelper
|
||||
end
|
||||
|
||||
def link_to_toggle_star(title, starred, signed_in)
|
||||
cls = 'btn btn-block'
|
||||
cls = 'star-btn'
|
||||
cls += ' disabled' unless signed_in
|
||||
|
||||
toggle_html = content_tag('span', class: 'toggle') do
|
||||
@@ -151,11 +151,19 @@ module ProjectsHelper
|
||||
|
||||
content_tag 'span', class: starred ? 'turn-on' : 'turn-off' do
|
||||
link_to toggle_star_project_path(@project), link_opts do
|
||||
toggle_html + count_html
|
||||
toggle_html + ' ' + count_html
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def link_to_toggle_fork
|
||||
out = content_tag(:i, '', class: 'icon-code-fork')
|
||||
out << ' Fork'
|
||||
out << content_tag(:span, class: 'count') do
|
||||
@project.forks_count.to_s
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def get_project_nav_tabs(project, current_user)
|
||||
@@ -261,4 +269,10 @@ module ProjectsHelper
|
||||
project_blob_path(project, tree_join(project.default_branch, project.repository.contribution_guide.name))
|
||||
end
|
||||
end
|
||||
|
||||
def hidden_pass_url(original_url)
|
||||
result = URI(original_url)
|
||||
result.password = '*****' if result.password.present?
|
||||
result
|
||||
end
|
||||
end
|
||||
|
||||
@@ -80,7 +80,8 @@ module SearchHelper
|
||||
|
||||
# Autocomplete results for the current user's projects
|
||||
def projects_autocomplete(term, limit = 5)
|
||||
ProjectsFinder.new.execute(current_user).search_by_title(term).non_archived.limit(limit).map do |p|
|
||||
ProjectsFinder.new.execute(current_user).search_by_title(term).
|
||||
sorted_by_stars.non_archived.limit(limit).map do |p|
|
||||
{
|
||||
label: "project: #{search_result_sanitize(p.name_with_namespace)}",
|
||||
url: project_path(p)
|
||||
@@ -91,4 +92,21 @@ module SearchHelper
|
||||
def search_result_sanitize(str)
|
||||
Sanitize.clean(str)
|
||||
end
|
||||
|
||||
def search_filter_path(options={})
|
||||
exist_opts = {
|
||||
search: params[:search],
|
||||
project_id: params[:project_id],
|
||||
group_id: params[:group_id],
|
||||
scope: params[:scope]
|
||||
}
|
||||
|
||||
options = exist_opts.merge(options)
|
||||
search_path(options)
|
||||
end
|
||||
|
||||
# Sanitize html generated after parsing markdown from issue description or comment
|
||||
def search_md_sanitize(html)
|
||||
sanitize(html, tags: %w(a p ol ul li pre code))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
module Emails
|
||||
module Groups
|
||||
def group_access_granted_email(user_group_id)
|
||||
@membership = UsersGroup.find(user_group_id)
|
||||
@membership = GroupMember.find(user_group_id)
|
||||
@group = @membership.group
|
||||
@target_url = group_url(@group)
|
||||
mail(to: @membership.user.email,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
module Emails
|
||||
module Projects
|
||||
def project_access_granted_email(user_project_id)
|
||||
@users_project = UsersProject.find user_project_id
|
||||
@project = @users_project.project
|
||||
@project_member = ProjectMember.find user_project_id
|
||||
@project = @project_member.project
|
||||
@target_url = project_url(@project)
|
||||
mail(to: @users_project.user.email,
|
||||
mail(to: @project_member.user.email,
|
||||
subject: subject("Access to project was granted"))
|
||||
end
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ class Ability
|
||||
when "MergeRequest" then merge_request_abilities(user, subject)
|
||||
when "Group" then group_abilities(user, subject)
|
||||
when "Namespace" then namespace_abilities(user, subject)
|
||||
when "UsersGroup" then users_group_abilities(user, subject)
|
||||
when "GroupMember" then users_group_abilities(user, subject)
|
||||
else []
|
||||
end.concat(global_abilities(user))
|
||||
end
|
||||
|
||||
@@ -108,4 +108,8 @@ class Commit
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
def parents
|
||||
@parents ||= Commit.decorate(super)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -49,6 +49,10 @@ module Issuable
|
||||
where("LOWER(title) like :query", query: "%#{query.downcase}%")
|
||||
end
|
||||
|
||||
def full_search(query)
|
||||
where("LOWER(title) like :query OR LOWER(description) like :query", query: "%#{query.downcase}%")
|
||||
end
|
||||
|
||||
def sort(method)
|
||||
case method.to_s
|
||||
when 'newest' then reorder("#{table_name}.created_at DESC")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# == Notifiable concern
|
||||
#
|
||||
# Contains notification functionality shared between UsersProject and UsersGroup
|
||||
# Contains notification functionality
|
||||
#
|
||||
module Notifiable
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
+10
-10
@@ -17,8 +17,8 @@ require 'carrierwave/orm/activerecord'
|
||||
require 'file_size_validator'
|
||||
|
||||
class Group < Namespace
|
||||
has_many :users_groups, dependent: :destroy
|
||||
has_many :users, through: :users_groups
|
||||
has_many :group_members, dependent: :destroy, as: :source, class_name: 'GroupMember'
|
||||
has_many :users, through: :group_members
|
||||
|
||||
validate :avatar_type, if: ->(user) { user.avatar_changed? }
|
||||
validates :avatar, file_size: { maximum: 100.kilobytes.to_i }
|
||||
@@ -30,22 +30,22 @@ class Group < Namespace
|
||||
end
|
||||
|
||||
def owners
|
||||
@owners ||= users_groups.owners.map(&:user)
|
||||
@owners ||= group_members.owners.map(&:user)
|
||||
end
|
||||
|
||||
def add_users(user_ids, group_access)
|
||||
def add_users(user_ids, access_level)
|
||||
user_ids.compact.each do |user_id|
|
||||
user = self.users_groups.find_or_initialize_by(user_id: user_id)
|
||||
user.update_attributes(group_access: group_access)
|
||||
user = self.group_members.find_or_initialize_by(user_id: user_id)
|
||||
user.update_attributes(access_level: access_level)
|
||||
end
|
||||
end
|
||||
|
||||
def add_user(user, group_access)
|
||||
self.users_groups.create(user_id: user.id, group_access: group_access)
|
||||
def add_user(user, access_level)
|
||||
self.group_members.create(user_id: user.id, access_level: access_level)
|
||||
end
|
||||
|
||||
def add_owner(user)
|
||||
self.add_user(user, UsersGroup::OWNER)
|
||||
self.add_user(user, Gitlab::Access::OWNER)
|
||||
end
|
||||
|
||||
def has_owner?(user)
|
||||
@@ -61,7 +61,7 @@ class Group < Namespace
|
||||
end
|
||||
|
||||
def members
|
||||
users_groups
|
||||
group_members
|
||||
end
|
||||
|
||||
def avatar_type
|
||||
|
||||
@@ -23,7 +23,7 @@ class WebHook < ActiveRecord::Base
|
||||
default_value_for :merge_requests_events, false
|
||||
|
||||
# HTTParty timeout
|
||||
default_timeout 10
|
||||
default_timeout Gitlab.config.gitlab.webhook_timeout
|
||||
|
||||
validates :url, presence: true,
|
||||
format: { with: URI::regexp(%w(http https)), message: "should be a valid url" }
|
||||
@@ -29,7 +29,9 @@ class Key < ActiveRecord::Base
|
||||
|
||||
after_create :add_to_shell
|
||||
after_create :notify_user
|
||||
after_create :post_create_hook
|
||||
after_destroy :remove_from_shell
|
||||
after_destroy :post_destroy_hook
|
||||
|
||||
def strip_white_space
|
||||
self.key = key.strip unless key.blank?
|
||||
@@ -56,6 +58,10 @@ class Key < ActiveRecord::Base
|
||||
NotificationService.new.new_key(self)
|
||||
end
|
||||
|
||||
def post_create_hook
|
||||
SystemHooksService.new.execute_hooks_for(self, :create)
|
||||
end
|
||||
|
||||
def remove_from_shell
|
||||
GitlabShellWorker.perform_async(
|
||||
:remove_key,
|
||||
@@ -64,6 +70,10 @@ class Key < ActiveRecord::Base
|
||||
)
|
||||
end
|
||||
|
||||
def post_destroy_hook
|
||||
SystemHooksService.new.execute_hooks_for(self, :destroy)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def generate_fingerpint
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
class Member < ActiveRecord::Base
|
||||
include Notifiable
|
||||
include Gitlab::Access
|
||||
|
||||
belongs_to :user
|
||||
belongs_to :source, polymorphic: true
|
||||
|
||||
validates :user, presence: true
|
||||
validates :source, presence: true
|
||||
validates :user_id, uniqueness: { scope: [:source_type, :source_id], message: "already exists in source" }
|
||||
validates :access_level, inclusion: { in: Gitlab::Access.all_values }, presence: true
|
||||
|
||||
scope :guests, -> { where(access_level: GUEST) }
|
||||
scope :reporters, -> { where(access_level: REPORTER) }
|
||||
scope :developers, -> { where(access_level: DEVELOPER) }
|
||||
scope :masters, -> { where(access_level: MASTER) }
|
||||
scope :owners, -> { where(access_level: OWNER) }
|
||||
|
||||
delegate :name, :username, :email, to: :user, prefix: true
|
||||
end
|
||||
@@ -0,0 +1,43 @@
|
||||
class GroupMember < Member
|
||||
SOURCE_TYPE = 'Namespace'
|
||||
|
||||
belongs_to :group, class_name: 'Group', foreign_key: 'source_id'
|
||||
|
||||
# Make sure group member points only to group as it source
|
||||
default_value_for :source_type, SOURCE_TYPE
|
||||
default_value_for :notification_level, Notification::N_GLOBAL
|
||||
validates_format_of :source_type, with: /\ANamespace\z/
|
||||
default_scope { where(source_type: SOURCE_TYPE) }
|
||||
|
||||
scope :with_group, ->(group) { where(source_id: group.id) }
|
||||
scope :with_user, ->(user) { where(user_id: user.id) }
|
||||
|
||||
after_create :notify_create
|
||||
after_update :notify_update
|
||||
|
||||
def self.access_level_roles
|
||||
Gitlab::Access.options_with_owner
|
||||
end
|
||||
|
||||
def group
|
||||
source
|
||||
end
|
||||
|
||||
def access_field
|
||||
access_level
|
||||
end
|
||||
|
||||
def notify_create
|
||||
notification_service.new_group_member(self)
|
||||
end
|
||||
|
||||
def notify_update
|
||||
if access_level_changed?
|
||||
notification_service.update_group_member(self)
|
||||
end
|
||||
end
|
||||
|
||||
def notification_service
|
||||
NotificationService.new
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,137 @@
|
||||
class ProjectMember < Member
|
||||
SOURCE_TYPE = 'Project'
|
||||
|
||||
include Gitlab::ShellAdapter
|
||||
|
||||
belongs_to :project, class_name: 'Project', foreign_key: 'source_id'
|
||||
|
||||
|
||||
# Make sure project member points only to project as it source
|
||||
default_value_for :source_type, SOURCE_TYPE
|
||||
default_value_for :notification_level, Notification::N_GLOBAL
|
||||
validates_format_of :source_type, with: /\AProject\z/
|
||||
default_scope { where(source_type: SOURCE_TYPE) }
|
||||
|
||||
after_create :post_create_hook
|
||||
after_update :post_update_hook
|
||||
after_destroy :post_destroy_hook
|
||||
|
||||
scope :in_project, ->(project) { where(source_id: project.id) }
|
||||
scope :in_projects, ->(projects) { where(source_id: projects.pluck(:id)) }
|
||||
scope :with_user, ->(user) { where(user_id: user.id) }
|
||||
|
||||
class << self
|
||||
|
||||
# Add users to project teams with passed access option
|
||||
#
|
||||
# access can be an integer representing a access code
|
||||
# or symbol like :master representing role
|
||||
#
|
||||
# Ex.
|
||||
# add_users_into_projects(
|
||||
# project_ids,
|
||||
# user_ids,
|
||||
# ProjectMember::MASTER
|
||||
# )
|
||||
#
|
||||
# add_users_into_projects(
|
||||
# project_ids,
|
||||
# user_ids,
|
||||
# :master
|
||||
# )
|
||||
#
|
||||
def add_users_into_projects(project_ids, user_ids, access)
|
||||
access_level = if roles_hash.has_key?(access)
|
||||
roles_hash[access]
|
||||
elsif roles_hash.values.include?(access.to_i)
|
||||
access
|
||||
else
|
||||
raise "Non valid access"
|
||||
end
|
||||
|
||||
ProjectMember.transaction do
|
||||
project_ids.each do |project_id|
|
||||
user_ids.each do |user_id|
|
||||
member = ProjectMember.new(access_level: access_level, user_id: user_id)
|
||||
member.source_id = project_id
|
||||
member.save
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
true
|
||||
rescue
|
||||
false
|
||||
end
|
||||
|
||||
def truncate_teams(project_ids)
|
||||
ProjectMember.transaction do
|
||||
members = ProjectMember.where(source_id: project_ids)
|
||||
members.each do |member|
|
||||
member.destroy
|
||||
end
|
||||
end
|
||||
|
||||
true
|
||||
rescue
|
||||
false
|
||||
end
|
||||
|
||||
def truncate_team project
|
||||
truncate_teams [project.id]
|
||||
end
|
||||
|
||||
def roles_hash
|
||||
Gitlab::Access.sym_options
|
||||
end
|
||||
|
||||
def access_roles
|
||||
Gitlab::Access.options
|
||||
end
|
||||
end
|
||||
|
||||
def access_field
|
||||
access_level
|
||||
end
|
||||
|
||||
def owner?
|
||||
project.owner == user
|
||||
end
|
||||
|
||||
def post_create_hook
|
||||
Event.create(
|
||||
project_id: self.project.id,
|
||||
action: Event::JOINED,
|
||||
author_id: self.user.id
|
||||
)
|
||||
|
||||
notification_service.new_team_member(self) unless owner?
|
||||
system_hook_service.execute_hooks_for(self, :create)
|
||||
end
|
||||
|
||||
def post_update_hook
|
||||
notification_service.update_team_member(self) if self.access_level_changed?
|
||||
end
|
||||
|
||||
def post_destroy_hook
|
||||
Event.create(
|
||||
project_id: self.project.id,
|
||||
action: Event::LEFT,
|
||||
author_id: self.user.id
|
||||
)
|
||||
|
||||
system_hook_service.execute_hooks_for(self, :destroy)
|
||||
end
|
||||
|
||||
def notification_service
|
||||
NotificationService.new
|
||||
end
|
||||
|
||||
def system_hook_service
|
||||
SystemHooksService.new
|
||||
end
|
||||
|
||||
def project
|
||||
source
|
||||
end
|
||||
end
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user