Compare commits
10 commits
master
...
5-0-stable
Author | SHA1 | Date | |
---|---|---|---|
|
8b761574bd | ||
|
34136d381f | ||
|
65156e3693 | ||
|
6a09b21df6 | ||
|
4606380316 | ||
|
017c19bbe4 | ||
|
0c7f426037 | ||
|
5c49cf6ca1 | ||
|
0e4b2395a9 | ||
|
ef968fd0c8 |
208 changed files with 2229 additions and 2785 deletions
2
.rspec
2
.rspec
|
@ -1 +1 @@
|
||||||
--colour --drb
|
--colour
|
||||||
|
|
|
@ -8,7 +8,7 @@ branches:
|
||||||
only:
|
only:
|
||||||
- 'master'
|
- 'master'
|
||||||
rvm:
|
rvm:
|
||||||
- 1.9.3-p392
|
- 1.9.3-p327
|
||||||
services:
|
services:
|
||||||
- mysql
|
- mysql
|
||||||
- postgresql
|
- postgresql
|
||||||
|
|
|
@ -1,10 +1,3 @@
|
||||||
v 5.1.0
|
|
||||||
- You can login with email or username now
|
|
||||||
- Corrected project transfer rollback when repository cannot be moved
|
|
||||||
- Move both repo and wiki when project transfer requrested
|
|
||||||
- Admin area: project editing was removed from admin namespace
|
|
||||||
- Access: admin user has now access to any project.
|
|
||||||
|
|
||||||
v 5.0.0
|
v 5.0.0
|
||||||
- Replaced gitolite with gitlab-shell
|
- Replaced gitolite with gitlab-shell
|
||||||
- Removed gitolite-related libraries
|
- Removed gitolite-related libraries
|
||||||
|
@ -31,7 +24,7 @@ v 5.0.0
|
||||||
- Fixed search field on projects page
|
- Fixed search field on projects page
|
||||||
- Added teams to search autocomplete
|
- Added teams to search autocomplete
|
||||||
- Move groups and teams on dashboard sidebar to sub-tabs
|
- Move groups and teams on dashboard sidebar to sub-tabs
|
||||||
- API: improved return codes and docs. (Felix Gilcher, Sebastian Ziebell)
|
- API: improved return codes and docs.
|
||||||
- Redesign wall to be more like chat
|
- Redesign wall to be more like chat
|
||||||
- Snippets, Wall features are disabled by default for new projects
|
- Snippets, Wall features are disabled by default for new projects
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,35 @@
|
||||||
# Contribute to GitLab
|
# Contribute to GitLab
|
||||||
|
|
||||||
This guide details how to use pull requests and the issues to improve GitLab.
|
If you have a question or want to contribute to GitLab this guide show you the appropriate channel to use.
|
||||||
|
|
||||||
## Closing policy for pull requests and issues
|
## Ruling out common errors
|
||||||
|
|
||||||
Pull requests and issues not in line with the guidelines listed in this document will be closed with just a link to this paragraph. GitLab is a popular open source project and the capacity to deal with issues and pull requests is limited. To get support for your problems please use other channels as detailed in [the getting help section of the readme](https://github.com/gitlabhq/gitlabhq#getting-help). Professional [support subscriptions](http://www.gitlab.com/subscription/) and [consulting services](http://www.gitlab.com/consultancy/) are available from [GitLab.com](http://www.gitlab.com/).
|
Some errors are common and it may so happen, that you are not the only one who stumbled over a particular issue. We have [collected several of those and documented quick solutions](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide) for them.
|
||||||
|
|
||||||
|
## Support forum
|
||||||
|
|
||||||
|
Please visit our [Support Forum](https://groups.google.com/forum/#!forum/gitlabhq) for any kind of question regarding the usage or adiministration/configuration of GitLab.
|
||||||
|
|
||||||
|
### Use the support forum if ...
|
||||||
|
|
||||||
|
* You get permission denied errors
|
||||||
|
* You can't see your repos
|
||||||
|
* You have issues cloning, pulling or pushing
|
||||||
|
* You have issues with web_hooks not firing
|
||||||
|
|
||||||
|
**Search** for similar issues before posting your own, there's a good chance somebody else had the same issue you have now and had it resolved.
|
||||||
|
|
||||||
|
## Paid support
|
||||||
|
|
||||||
|
Community support in the [Support Forum](https://groups.google.com/forum/#!forum/gitlabhq) is done by volunteers. A support subscription is available from [GitLab.com](http://blog.gitlab.com/subscription/)
|
||||||
|
|
||||||
|
## Feature suggestions
|
||||||
|
|
||||||
|
Feature suggestions don't belong in issues but can go to [Feedback forum](http://gitlab.uservoice.com/forums/176466-general) where they can be voted on.
|
||||||
|
|
||||||
## Pull requests
|
## Pull requests
|
||||||
|
|
||||||
We welcome pull request with improvements to GitLab code and/or documentation. The issues we would really like a pull request for are listed with the [status 'accepting merge/pull requests' on our feedback forum](http://feedback.gitlab.com/forums/176466-general/status/796455) but other improvements are also welcome.
|
Code speaks louder than words. If you can please submit a pull request with the fix including tests. The workflow to make a pull request is as follows:
|
||||||
|
|
||||||
### Pull request guidelines
|
|
||||||
|
|
||||||
If you can please submit a pull request with the fix including tests. The workflow to make a pull request is as follows:
|
|
||||||
|
|
||||||
1. Fork the project on GitHub
|
1. Fork the project on GitHub
|
||||||
1. Create a feature branch
|
1. Create a feature branch
|
||||||
|
@ -34,20 +51,26 @@ We will accept pull requests if:
|
||||||
|
|
||||||
For examples of feedback on pull requests please look at already [closed pull requests](https://github.com/gitlabhq/gitlabhq/pulls?direction=desc&page=1&sort=created&state=closed).
|
For examples of feedback on pull requests please look at already [closed pull requests](https://github.com/gitlabhq/gitlabhq/pulls?direction=desc&page=1&sort=created&state=closed).
|
||||||
|
|
||||||
## Issue tracker
|
## Submitting via GitHub's issue tracker
|
||||||
|
|
||||||
The [issue tracker](https://github.com/gitlabhq/gitlabhq/issues) is only for obvious bugs or misbehavior in the master branch of GitLab. When submitting an issue please conform to the issue submission guidelines listed below.
|
* For obvious bugs or misbehavior in GitLab in the master branch. Please include the revision id and a reproducible test case.
|
||||||
|
* For problematic or insufficient documentation. Please give a suggestion on how to improve it.
|
||||||
|
|
||||||
Please send a pull request with a tested solution or a pull request with a failing test instead of opening an issue if you can. If you're unsure where to post, post to the [Support Forum](https://groups.google.com/forum/#!forum/gitlabhq) first. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there.
|
If you're unsure where to post, post it to the [Support Forum](https://groups.google.com/forum/#!forum/gitlabhq) first.
|
||||||
|
There are a lot of helpful GitLab users there who may be able to help you quickly.
|
||||||
|
If your particular issue turns out to be a bug, it will find its way from there to the [issue tracker on GitHub](https://github.com/gitlabhq/gitlabhq/issues).
|
||||||
|
|
||||||
### Issue tracker guidelines
|
### When submitting an issue
|
||||||
|
|
||||||
**Search** for similar entries before submitting your own, there's a good chance somebody else had the same issue or idea. Show your support with `:+1:` and/or join the discussion.
|
**Search** for similar entries before submitting your own, there's a good chance somebody else had the same issue or idea. Show your support with `:+1:` and/or join the discussion.
|
||||||
|
|
||||||
* Summarize your issue in one sentence (what goes wrong, what did you expect to happen)
|
Please consider the following points when submitting an **issue**:
|
||||||
* Describe your issue in detail
|
|
||||||
* How can we reproduce the issue on the [GitLab Vagrant virtual machine](https://github.com/gitlabhq/gitlab-vagrant-vm) (start with: vagrant destroy && vagrant up && vagrant ssh)
|
* Summarize your issue in one sentence (what happened wrong, when you did/expected something else)
|
||||||
* Add the last commit sha1 of the GitLab version you used to replicate the issue
|
* Describe your issue in detail (including steps to reproduce)
|
||||||
* Add logs or screen shots when possible
|
* Add logs or screen shots when possible
|
||||||
* Link to the line of code that might be responsible for the problem
|
|
||||||
* Describe your setup (use relevant parts from `sudo -u gitlab -H bundle exec rake gitlab:env:info`)
|
* Describe your setup (use relevant parts from `sudo -u gitlab -H bundle exec rake gitlab:env:info`)
|
||||||
|
|
||||||
|
## Thank you!
|
||||||
|
|
||||||
|
By taking the time to use the right channel, you help the development team to organize and prioritize issues and suggestions in order to make GitLab a better product for us all.
|
||||||
|
|
14
Gemfile
14
Gemfile
|
@ -23,8 +23,8 @@ gem 'omniauth-github'
|
||||||
|
|
||||||
# Extracting information from a git repository
|
# Extracting information from a git repository
|
||||||
# Since gollum requires grit we cannot use gitlab-grit gem name any more. Use grit instead
|
# Since gollum requires grit we cannot use gitlab-grit gem name any more. Use grit instead
|
||||||
gem "grit", '~> 2.5.0', git: 'https://github.com/gitlabhq/grit.git', ref: '42297cdcee16284d2e4eff23d41377f52fc28b9d'
|
gem "grit", '~> 2.5.0', git: 'https://github.com/gitlabhq/grit.git', ref: 'c40a32432616a07fa7fc3c32c24ab73ad6a9718f'
|
||||||
gem 'grit_ext', '~> 0.8.1'
|
gem 'grit_ext', '~> 0.6.2'
|
||||||
|
|
||||||
# Ruby/Rack Git Smart-HTTP Server Handler
|
# Ruby/Rack Git Smart-HTTP Server Handler
|
||||||
gem 'gitlab-grack', '~> 1.0.0', require: 'grack'
|
gem 'gitlab-grack', '~> 1.0.0', require: 'grack'
|
||||||
|
@ -105,7 +105,7 @@ gem 'settingslogic'
|
||||||
# github-linquist needs pygments 0.4.2 but Gollum 2.4.11
|
# github-linquist needs pygments 0.4.2 but Gollum 2.4.11
|
||||||
# requires pygments 0.3.2. The latest master Gollum has been updated
|
# requires pygments 0.3.2. The latest master Gollum has been updated
|
||||||
# to use pygments 0.4.2. Change this after next Gollum release.
|
# to use pygments 0.4.2. Change this after next Gollum release.
|
||||||
gem "gollum", "~> 2.4.0", git: "https://github.com/gollum/gollum.git", ref: "5dcd3c8c8f"
|
gem "gollum", "~> 2.4.0", git: "git://github.com/gollum/gollum.git", ref: "5dcd3c8c8f"
|
||||||
|
|
||||||
# Misc
|
# Misc
|
||||||
gem "foreman"
|
gem "foreman"
|
||||||
|
@ -154,9 +154,9 @@ end
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
gem 'coveralls', require: false
|
gem 'coveralls', require: false
|
||||||
gem 'rails-dev-tweaks'
|
gem 'rails-dev-tweaks'
|
||||||
gem 'spinach-rails'
|
gem 'spinach-rails', '0.2.0'
|
||||||
gem "rspec-rails"
|
gem "rspec-rails", '2.12.2'
|
||||||
gem "capybara"
|
gem "capybara", '2.0.2'
|
||||||
gem "pry"
|
gem "pry"
|
||||||
gem "awesome_print"
|
gem "awesome_print"
|
||||||
gem "database_cleaner"
|
gem "database_cleaner"
|
||||||
|
@ -174,8 +174,6 @@ group :development, :test do
|
||||||
|
|
||||||
# PhantomJS driver for Capybara
|
# PhantomJS driver for Capybara
|
||||||
gem 'poltergeist', '1.1.0'
|
gem 'poltergeist', '1.1.0'
|
||||||
|
|
||||||
gem 'spork', '~> 1.0rc'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
group :test do
|
group :test do
|
||||||
|
|
142
Gemfile.lock
142
Gemfile.lock
|
@ -1,29 +1,5 @@
|
||||||
GIT
|
GIT
|
||||||
remote: https://github.com/ctran/annotate_models.git
|
remote: git://github.com/gollum/gollum.git
|
||||||
revision: be4e26825b521f0b2d86b181e2dff89901aa9b1e
|
|
||||||
specs:
|
|
||||||
annotate (2.6.0.beta1)
|
|
||||||
activerecord (>= 2.3.0)
|
|
||||||
rake (>= 0.8.7)
|
|
||||||
|
|
||||||
GIT
|
|
||||||
remote: https://github.com/gitlabhq/grit.git
|
|
||||||
revision: 42297cdcee16284d2e4eff23d41377f52fc28b9d
|
|
||||||
ref: 42297cdcee16284d2e4eff23d41377f52fc28b9d
|
|
||||||
specs:
|
|
||||||
grit (2.5.0)
|
|
||||||
diff-lcs (~> 1.1)
|
|
||||||
mime-types (~> 1.15)
|
|
||||||
posix-spawn (~> 0.3.6)
|
|
||||||
|
|
||||||
GIT
|
|
||||||
remote: https://github.com/gitlabhq/raphael-rails.git
|
|
||||||
revision: cb2c92a040b9b941a5f1aa1ea866cc26e944fe58
|
|
||||||
specs:
|
|
||||||
raphael-rails (2.1.0)
|
|
||||||
|
|
||||||
GIT
|
|
||||||
remote: https://github.com/gollum/gollum.git
|
|
||||||
revision: 5dcd3c8c8f68158e43ff79861279088ee56d0ebe
|
revision: 5dcd3c8c8f68158e43ff79861279088ee56d0ebe
|
||||||
ref: 5dcd3c8c8f
|
ref: 5dcd3c8c8f
|
||||||
specs:
|
specs:
|
||||||
|
@ -39,6 +15,30 @@ GIT
|
||||||
stringex (~> 1.5.1)
|
stringex (~> 1.5.1)
|
||||||
useragent (~> 0.4.16)
|
useragent (~> 0.4.16)
|
||||||
|
|
||||||
|
GIT
|
||||||
|
remote: https://github.com/ctran/annotate_models.git
|
||||||
|
revision: be4e26825b521f0b2d86b181e2dff89901aa9b1e
|
||||||
|
specs:
|
||||||
|
annotate (2.6.0.beta1)
|
||||||
|
activerecord (>= 2.3.0)
|
||||||
|
rake (>= 0.8.7)
|
||||||
|
|
||||||
|
GIT
|
||||||
|
remote: https://github.com/gitlabhq/grit.git
|
||||||
|
revision: c40a32432616a07fa7fc3c32c24ab73ad6a9718f
|
||||||
|
ref: c40a32432616a07fa7fc3c32c24ab73ad6a9718f
|
||||||
|
specs:
|
||||||
|
grit (2.5.0)
|
||||||
|
diff-lcs (~> 1.1)
|
||||||
|
mime-types (~> 1.15)
|
||||||
|
posix-spawn (~> 0.3.6)
|
||||||
|
|
||||||
|
GIT
|
||||||
|
remote: https://github.com/gitlabhq/raphael-rails.git
|
||||||
|
revision: cb2c92a040b9b941a5f1aa1ea866cc26e944fe58
|
||||||
|
specs:
|
||||||
|
raphael-rails (2.1.0)
|
||||||
|
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
|
@ -105,7 +105,7 @@ GEM
|
||||||
thor (~> 0.14)
|
thor (~> 0.14)
|
||||||
code_analyzer (0.3.1)
|
code_analyzer (0.3.1)
|
||||||
sexp_processor
|
sexp_processor
|
||||||
coderay (1.0.9)
|
coderay (1.0.8)
|
||||||
coffee-rails (3.2.2)
|
coffee-rails (3.2.2)
|
||||||
coffee-script (>= 2.2.0)
|
coffee-script (>= 2.2.0)
|
||||||
railties (~> 3.2.0)
|
railties (~> 3.2.0)
|
||||||
|
@ -122,7 +122,7 @@ GEM
|
||||||
rest-client
|
rest-client
|
||||||
simplecov (>= 0.7)
|
simplecov (>= 0.7)
|
||||||
thor
|
thor
|
||||||
crack (0.3.2)
|
crack (0.3.1)
|
||||||
daemons (1.1.9)
|
daemons (1.1.9)
|
||||||
database_cleaner (0.9.1)
|
database_cleaner (0.9.1)
|
||||||
debug_inspector (0.0.2)
|
debug_inspector (0.0.2)
|
||||||
|
@ -132,7 +132,7 @@ GEM
|
||||||
orm_adapter (~> 0.1)
|
orm_adapter (~> 0.1)
|
||||||
railties (~> 3.1)
|
railties (~> 3.1)
|
||||||
warden (~> 1.2.1)
|
warden (~> 1.2.1)
|
||||||
diff-lcs (1.2.1)
|
diff-lcs (1.1.3)
|
||||||
draper (1.1.0)
|
draper (1.1.0)
|
||||||
actionpack (>= 3.0)
|
actionpack (>= 3.0)
|
||||||
activesupport (>= 3.0)
|
activesupport (>= 3.0)
|
||||||
|
@ -147,7 +147,7 @@ GEM
|
||||||
eventmachine (1.0.0)
|
eventmachine (1.0.0)
|
||||||
execjs (1.4.0)
|
execjs (1.4.0)
|
||||||
multi_json (~> 1.0)
|
multi_json (~> 1.0)
|
||||||
facter (1.6.18)
|
facter (1.6.17)
|
||||||
factory_girl (4.1.0)
|
factory_girl (4.1.0)
|
||||||
activesupport (>= 3.0.0)
|
activesupport (>= 3.0.0)
|
||||||
factory_girl_rails (4.1.0)
|
factory_girl_rails (4.1.0)
|
||||||
|
@ -200,16 +200,15 @@ GEM
|
||||||
grape-entity (0.2.0)
|
grape-entity (0.2.0)
|
||||||
activesupport
|
activesupport
|
||||||
multi_json (>= 1.3.2)
|
multi_json (>= 1.3.2)
|
||||||
grit_ext (0.8.1)
|
grit_ext (0.6.2)
|
||||||
charlock_holmes (~> 0.6.9)
|
charlock_holmes (~> 0.6.9)
|
||||||
growl (1.0.3)
|
growl (1.0.3)
|
||||||
guard (1.6.2)
|
guard (1.5.4)
|
||||||
listen (>= 0.6.0)
|
listen (>= 0.4.2)
|
||||||
lumberjack (>= 1.0.2)
|
lumberjack (>= 1.0.2)
|
||||||
pry (>= 0.9.10)
|
pry (>= 0.9.10)
|
||||||
terminal-table (>= 1.4.3)
|
|
||||||
thor (>= 0.14.6)
|
thor (>= 0.14.6)
|
||||||
guard-rspec (2.5.1)
|
guard-rspec (2.1.2)
|
||||||
guard (>= 1.1)
|
guard (>= 1.1)
|
||||||
rspec (~> 2.11)
|
rspec (~> 2.11)
|
||||||
guard-spinach (0.0.2)
|
guard-spinach (0.0.2)
|
||||||
|
@ -249,9 +248,9 @@ GEM
|
||||||
addressable (~> 2.3)
|
addressable (~> 2.3)
|
||||||
letter_opener (1.0.0)
|
letter_opener (1.0.0)
|
||||||
launchy (>= 2.0.4)
|
launchy (>= 2.0.4)
|
||||||
libv8 (3.11.8.17)
|
libv8 (3.3.10.4)
|
||||||
listen (0.7.3)
|
listen (0.5.3)
|
||||||
lumberjack (1.0.3)
|
lumberjack (1.0.2)
|
||||||
mail (2.5.3)
|
mail (2.5.3)
|
||||||
i18n (>= 0.4.0)
|
i18n (>= 0.4.0)
|
||||||
mime-types (~> 1.16)
|
mime-types (~> 1.16)
|
||||||
|
@ -260,7 +259,7 @@ GEM
|
||||||
mime-types (1.21)
|
mime-types (1.21)
|
||||||
modernizr (2.6.2)
|
modernizr (2.6.2)
|
||||||
sprockets (~> 2.0)
|
sprockets (~> 2.0)
|
||||||
multi_json (1.7.2)
|
multi_json (1.7.1)
|
||||||
multi_xml (0.5.3)
|
multi_xml (0.5.3)
|
||||||
multipart-post (1.1.5)
|
multipart-post (1.1.5)
|
||||||
mustache (0.99.4)
|
mustache (0.99.4)
|
||||||
|
@ -300,10 +299,11 @@ GEM
|
||||||
http_parser.rb (~> 0.5.3)
|
http_parser.rb (~> 0.5.3)
|
||||||
polyglot (0.3.3)
|
polyglot (0.3.3)
|
||||||
posix-spawn (0.3.6)
|
posix-spawn (0.3.6)
|
||||||
pry (0.9.12)
|
progressbar (0.12.0)
|
||||||
|
pry (0.9.10)
|
||||||
coderay (~> 1.0.5)
|
coderay (~> 1.0.5)
|
||||||
method_source (~> 0.8)
|
method_source (~> 0.8)
|
||||||
slop (~> 3.4)
|
slop (~> 3.3.1)
|
||||||
pygments.rb (0.4.2)
|
pygments.rb (0.4.2)
|
||||||
posix-spawn (~> 0.3.6)
|
posix-spawn (~> 0.3.6)
|
||||||
yajl-ruby (~> 1.1.0)
|
yajl-ruby (~> 1.1.0)
|
||||||
|
@ -336,14 +336,14 @@ GEM
|
||||||
rails-dev-tweaks (0.6.1)
|
rails-dev-tweaks (0.6.1)
|
||||||
actionpack (~> 3.1)
|
actionpack (~> 3.1)
|
||||||
railties (~> 3.1)
|
railties (~> 3.1)
|
||||||
rails_best_practices (1.13.4)
|
rails_best_practices (1.13.2)
|
||||||
activesupport
|
activesupport
|
||||||
awesome_print
|
awesome_print
|
||||||
code_analyzer
|
code_analyzer
|
||||||
colored
|
colored
|
||||||
erubis
|
erubis
|
||||||
i18n
|
i18n
|
||||||
ruby-progressbar
|
progressbar
|
||||||
railties (3.2.13)
|
railties (3.2.13)
|
||||||
actionpack (= 3.2.13)
|
actionpack (= 3.2.13)
|
||||||
activesupport (= 3.2.13)
|
activesupport (= 3.2.13)
|
||||||
|
@ -352,14 +352,14 @@ GEM
|
||||||
rdoc (~> 3.4)
|
rdoc (~> 3.4)
|
||||||
thor (>= 0.14.6, < 2.0)
|
thor (>= 0.14.6, < 2.0)
|
||||||
raindrops (0.10.0)
|
raindrops (0.10.0)
|
||||||
rake (10.0.4)
|
rake (10.0.3)
|
||||||
rb-fsevent (0.9.2)
|
rb-fsevent (0.9.2)
|
||||||
rb-inotify (0.8.8)
|
rb-inotify (0.8.8)
|
||||||
ffi (>= 0.5.0)
|
ffi (>= 0.5.0)
|
||||||
rdoc (3.12.2)
|
rdoc (3.12.2)
|
||||||
json (~> 1.4)
|
json (~> 1.4)
|
||||||
redcarpet (2.2.2)
|
redcarpet (2.2.2)
|
||||||
redis (3.0.3)
|
redis (3.0.2)
|
||||||
redis-actionpack (3.2.3)
|
redis-actionpack (3.2.3)
|
||||||
actionpack (~> 3.2.3)
|
actionpack (~> 3.2.3)
|
||||||
redis-rack (~> 1.4.0)
|
redis-rack (~> 1.4.0)
|
||||||
|
@ -378,32 +378,30 @@ GEM
|
||||||
redis-store (~> 1.1.0)
|
redis-store (~> 1.1.0)
|
||||||
redis-store (1.1.3)
|
redis-store (1.1.3)
|
||||||
redis (>= 2.2.0)
|
redis (>= 2.2.0)
|
||||||
ref (1.0.4)
|
|
||||||
request_store (1.0.5)
|
request_store (1.0.5)
|
||||||
rest-client (1.6.7)
|
rest-client (1.6.7)
|
||||||
mime-types (>= 1.16)
|
mime-types (>= 1.16)
|
||||||
rspec (2.13.0)
|
rspec (2.12.0)
|
||||||
rspec-core (~> 2.13.0)
|
rspec-core (~> 2.12.0)
|
||||||
rspec-expectations (~> 2.13.0)
|
rspec-expectations (~> 2.12.0)
|
||||||
rspec-mocks (~> 2.13.0)
|
rspec-mocks (~> 2.12.0)
|
||||||
rspec-core (2.13.1)
|
rspec-core (2.12.0)
|
||||||
rspec-expectations (2.13.0)
|
rspec-expectations (2.12.0)
|
||||||
diff-lcs (>= 1.1.3, < 2.0)
|
diff-lcs (~> 1.1.3)
|
||||||
rspec-mocks (2.13.0)
|
rspec-mocks (2.12.0)
|
||||||
rspec-rails (2.13.0)
|
rspec-rails (2.12.2)
|
||||||
actionpack (>= 3.0)
|
actionpack (>= 3.0)
|
||||||
activesupport (>= 3.0)
|
activesupport (>= 3.0)
|
||||||
railties (>= 3.0)
|
railties (>= 3.0)
|
||||||
rspec-core (~> 2.13.0)
|
rspec-core (~> 2.12.0)
|
||||||
rspec-expectations (~> 2.13.0)
|
rspec-expectations (~> 2.12.0)
|
||||||
rspec-mocks (~> 2.13.0)
|
rspec-mocks (~> 2.12.0)
|
||||||
ruby-progressbar (1.0.2)
|
|
||||||
rubyntlm (0.1.1)
|
rubyntlm (0.1.1)
|
||||||
rubyzip (0.9.9)
|
rubyzip (0.9.9)
|
||||||
sanitize (2.0.3)
|
sanitize (2.0.3)
|
||||||
nokogiri (>= 1.4.4, < 1.6)
|
nokogiri (>= 1.4.4, < 1.6)
|
||||||
sass (3.2.7)
|
sass (3.2.5)
|
||||||
sass-rails (3.2.6)
|
sass-rails (3.2.5)
|
||||||
railties (~> 3.2.0)
|
railties (~> 3.2.0)
|
||||||
sass (>= 3.1.10)
|
sass (>= 3.1.10)
|
||||||
tilt (~> 1.3)
|
tilt (~> 1.3)
|
||||||
|
@ -422,10 +420,10 @@ GEM
|
||||||
rubyzip
|
rubyzip
|
||||||
websocket (~> 1.0.4)
|
websocket (~> 1.0.4)
|
||||||
settingslogic (2.0.9)
|
settingslogic (2.0.9)
|
||||||
sexp_processor (4.2.0)
|
sexp_processor (4.1.3)
|
||||||
shoulda-matchers (1.3.0)
|
shoulda-matchers (1.3.0)
|
||||||
activesupport (>= 3.0.0)
|
activesupport (>= 3.0.0)
|
||||||
sidekiq (2.8.0)
|
sidekiq (2.7.5)
|
||||||
celluloid (~> 0.12.0)
|
celluloid (~> 0.12.0)
|
||||||
connection_pool (~> 1.0)
|
connection_pool (~> 1.0)
|
||||||
multi_json (~> 1)
|
multi_json (~> 1)
|
||||||
|
@ -443,7 +441,7 @@ GEM
|
||||||
slim (1.3.6)
|
slim (1.3.6)
|
||||||
temple (~> 0.5.5)
|
temple (~> 0.5.5)
|
||||||
tilt (~> 1.3.3)
|
tilt (~> 1.3.3)
|
||||||
slop (3.4.4)
|
slop (3.3.3)
|
||||||
spinach (0.7.0)
|
spinach (0.7.0)
|
||||||
colorize
|
colorize
|
||||||
gherkin-ruby (~> 0.2.0)
|
gherkin-ruby (~> 0.2.0)
|
||||||
|
@ -451,7 +449,6 @@ GEM
|
||||||
capybara (~> 2.0.0)
|
capybara (~> 2.0.0)
|
||||||
railties (>= 3)
|
railties (>= 3)
|
||||||
spinach (>= 0.4)
|
spinach (>= 0.4)
|
||||||
spork (1.0.0rc3)
|
|
||||||
sprockets (2.2.2)
|
sprockets (2.2.2)
|
||||||
hike (~> 1.2)
|
hike (~> 1.2)
|
||||||
multi_json (~> 1.0)
|
multi_json (~> 1.0)
|
||||||
|
@ -461,16 +458,14 @@ GEM
|
||||||
state_machine (1.1.2)
|
state_machine (1.1.2)
|
||||||
stringex (1.5.1)
|
stringex (1.5.1)
|
||||||
temple (0.5.5)
|
temple (0.5.5)
|
||||||
terminal-table (1.4.5)
|
|
||||||
test_after_commit (0.0.1)
|
test_after_commit (0.0.1)
|
||||||
therubyracer (0.11.4)
|
therubyracer (0.10.2)
|
||||||
libv8 (~> 3.11.8.12)
|
libv8 (~> 3.3.10)
|
||||||
ref
|
|
||||||
thin (1.5.0)
|
thin (1.5.0)
|
||||||
daemons (>= 1.0.9)
|
daemons (>= 1.0.9)
|
||||||
eventmachine (>= 0.12.6)
|
eventmachine (>= 0.12.6)
|
||||||
rack (>= 1.0.0)
|
rack (>= 1.0.0)
|
||||||
thor (0.18.0)
|
thor (0.17.0)
|
||||||
tilt (1.3.6)
|
tilt (1.3.6)
|
||||||
timers (1.1.0)
|
timers (1.1.0)
|
||||||
treetop (1.4.12)
|
treetop (1.4.12)
|
||||||
|
@ -508,7 +503,7 @@ DEPENDENCIES
|
||||||
better_errors
|
better_errors
|
||||||
binding_of_caller
|
binding_of_caller
|
||||||
bootstrap-sass (= 2.2.1.1)
|
bootstrap-sass (= 2.2.1.1)
|
||||||
capybara
|
capybara (= 2.0.2)
|
||||||
carrierwave
|
carrierwave
|
||||||
chosen-rails (= 0.9.8)
|
chosen-rails (= 0.9.8)
|
||||||
coffee-rails (~> 3.2.2)
|
coffee-rails (~> 3.2.2)
|
||||||
|
@ -537,7 +532,7 @@ DEPENDENCIES
|
||||||
grape (~> 0.3.1)
|
grape (~> 0.3.1)
|
||||||
grape-entity (~> 0.2.0)
|
grape-entity (~> 0.2.0)
|
||||||
grit (~> 2.5.0)!
|
grit (~> 2.5.0)!
|
||||||
grit_ext (~> 0.8.1)
|
grit_ext (~> 0.6.2)
|
||||||
growl
|
growl
|
||||||
guard-rspec
|
guard-rspec
|
||||||
guard-spinach
|
guard-spinach
|
||||||
|
@ -568,7 +563,7 @@ DEPENDENCIES
|
||||||
rb-inotify
|
rb-inotify
|
||||||
redcarpet (~> 2.2.2)
|
redcarpet (~> 2.2.2)
|
||||||
redis-rails
|
redis-rails
|
||||||
rspec-rails
|
rspec-rails (= 2.12.2)
|
||||||
sass-rails (~> 3.2.5)
|
sass-rails (~> 3.2.5)
|
||||||
sdoc
|
sdoc
|
||||||
seed-fu
|
seed-fu
|
||||||
|
@ -580,8 +575,7 @@ DEPENDENCIES
|
||||||
sinatra
|
sinatra
|
||||||
six
|
six
|
||||||
slim
|
slim
|
||||||
spinach-rails
|
spinach-rails (= 0.2.0)
|
||||||
spork (~> 1.0rc)
|
|
||||||
stamp
|
stamp
|
||||||
state_machine
|
state_machine
|
||||||
test_after_commit
|
test_after_commit
|
||||||
|
|
82
README.md
82
README.md
|
@ -12,7 +12,7 @@
|
||||||
|
|
||||||
* powered by Ruby on Rails
|
* powered by Ruby on Rails
|
||||||
* completely free and open source (MIT license)
|
* completely free and open source (MIT license)
|
||||||
* used by more than 10.000 organizations to keep their code secure
|
* used by 10.000 organizations to keep their code secure
|
||||||
|
|
||||||
### Code status
|
### Code status
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@
|
||||||
|
|
||||||
* GitLab.org community site: [Homepage](http://gitlab.org) [Screenshots](http://gitlab.org/screenshots/) [Blog](http://blog.gitlab.org/) [Demo](http://demo.gitlabhq.com/users/sign_in)
|
* GitLab.org community site: [Homepage](http://gitlab.org) [Screenshots](http://gitlab.org/screenshots/) [Blog](http://blog.gitlab.org/) [Demo](http://demo.gitlabhq.com/users/sign_in)
|
||||||
|
|
||||||
* GitLab.com commercial services: [Homepage](http://www.gitlab.com/) [Subscription](http://www.gitlab.com/subscription/) [Consultancy](http://www.gitlab.com/consultancy/) [GitLab Cloud](http://www.gitlab.com/cloud/) [Blog](http://blog.gitlab.com/)
|
* GitLab.com commercial services: [Homepage](http://blog.gitlab.com/) [GitLab Cloud](http://blog.gitlab.com/cloud/) [Subscription](http://blog.gitlab.com/subscription/) [Consultancy](http://blog.gitlab.com/consultancy/) [Blog](http://blog.gitlab.com/blog/)
|
||||||
|
|
||||||
* GitLab CI: [Readme](https://github.com/gitlabhq/gitlab-ci/blob/master/README.md) of the GitLab open-source continuous integration server
|
* GitLab CI: [Readme](https://github.com/gitlabhq/gitlab-ci/blob/master/README.md) of the GitLab open-source continuous integration server
|
||||||
|
|
||||||
|
@ -47,45 +47,22 @@
|
||||||
|
|
||||||
### Installation
|
### Installation
|
||||||
|
|
||||||
#### Official production installation
|
#### For production
|
||||||
|
|
||||||
Follow the installation guide for production server.
|
Follow the installation guide for production server.
|
||||||
|
|
||||||
* [Installation guide for latest stable release (5.0)](https://github.com/gitlabhq/gitlabhq/blob/5-0-stable/doc/install/installation.md) - **Recommended**
|
* [Installation guide for latest stable release (4.2)](https://github.com/gitlabhq/gitlabhq/blob/4-2-stable/doc/install/installation.md) - **Recommended**
|
||||||
|
|
||||||
* [Installation guide for the current master branch (5.1)](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/installation.md)
|
* [Installation guide for the current master branch (5.0)](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/installation.md)
|
||||||
|
|
||||||
|
|
||||||
#### Official development installation
|
#### For development
|
||||||
|
|
||||||
If you want to contribute, please first read our [Contributing Guidelines](https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md) and then we suggest you to use the Vagrant virtual machine project to get an environment working with all dependencies.
|
If you want to contribute, please first read our [Contributing Guidelines](https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md) and then we suggest you to use the Vagrant virtual machine project to get an environment working sandboxed and with all dependencies.
|
||||||
|
|
||||||
* [Vagrant virtual machine](https://github.com/gitlabhq/gitlab-vagrant-vm)
|
* [Vagrant virtual machine](https://github.com/gitlabhq/gitlab-vagrant-vm)
|
||||||
|
|
||||||
|
### Starting
|
||||||
#### Unsupported production installation
|
|
||||||
|
|
||||||
* [GitLab recipes](https://github.com/gitlabhq/gitlab-recipes) for setup on different platforms
|
|
||||||
|
|
||||||
* [Unofficial installation guides](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Unofficial-Installation-Guides)
|
|
||||||
|
|
||||||
* [BitNami one-click installers](http://bitnami.com/stack/gitlab)
|
|
||||||
|
|
||||||
* [TurnKey Linux virtual appliance](http://www.turnkeylinux.org/gitlab)
|
|
||||||
|
|
||||||
|
|
||||||
### New versions and upgrading
|
|
||||||
|
|
||||||
Each month on the 22th a new version is released together with an upgrade guide.
|
|
||||||
|
|
||||||
* [Upgrade guides](https://github.com/gitlabhq/gitlabhq/wiki)
|
|
||||||
|
|
||||||
* [Changelog](https://github.com/gitlabhq/gitlabhq/blob/master/CHANGELOG)
|
|
||||||
|
|
||||||
* [Roadmap](https://github.com/gitlabhq/gitlabhq/blob/master/ROADMAP.md)
|
|
||||||
|
|
||||||
|
|
||||||
### Getting started
|
|
||||||
|
|
||||||
1. The Installation guide contains instructions to download an init script and run that on boot. With the init script you can also start GitLab
|
1. The Installation guide contains instructions to download an init script and run that on boot. With the init script you can also start GitLab
|
||||||
|
|
||||||
|
@ -123,35 +100,38 @@ Each month on the 22th a new version is released together with an upgrade guide.
|
||||||
|
|
||||||
bundle exec rake spinach
|
bundle exec rake spinach
|
||||||
|
|
||||||
|
### Getting help
|
||||||
|
|
||||||
### GitLab interfaces
|
* [Troubleshooting guide](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide)
|
||||||
|
|
||||||
|
* [Support forum](https://groups.google.com/forum/#!forum/gitlabhq)
|
||||||
|
|
||||||
|
* [Feedback and suggestions forum](http://gitlab.uservoice.com/forums/176466-general)
|
||||||
|
|
||||||
|
* [Support subscription](http://blog.gitlab.com/subscription/)
|
||||||
|
|
||||||
|
* [Consultancy](http://blog.gitlab.com/consultancy/)
|
||||||
|
|
||||||
|
### New versions and the API
|
||||||
|
|
||||||
|
Each month on the 22th a new version is released together with an upgrade guide.
|
||||||
|
|
||||||
|
* [Upgrade guides](https://github.com/gitlabhq/gitlabhq/wiki)
|
||||||
|
|
||||||
|
* [Roadmap](https://github.com/gitlabhq/gitlabhq/blob/master/ROADMAP.md)
|
||||||
|
|
||||||
|
### Other documentation
|
||||||
|
|
||||||
* [GitLab API](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/README.md)
|
* [GitLab API](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/README.md)
|
||||||
|
|
||||||
* [Rake tasks](https://github.com/gitlabhq/gitlabhq/tree/master/doc/raketasks)
|
* [Rake tasks](https://github.com/gitlabhq/gitlabhq/tree/master/doc/raketasks)
|
||||||
|
|
||||||
* [Directory structure](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/structure.md)
|
* [GitLab recipes](https://github.com/gitlabhq/gitlab-recipes)
|
||||||
|
|
||||||
* [Databases](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/databases.md)
|
|
||||||
|
|
||||||
|
|
||||||
### Getting help
|
|
||||||
|
|
||||||
* [Troubleshooting guide](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide) contains solutions to common problems.
|
|
||||||
|
|
||||||
* [Support forum](https://groups.google.com/forum/#!forum/gitlabhq) is the best place to ask questions. For example you can use it if you have questions about: permission denied errors, invisible repos, can't clone/pull/push or with web hooks that don't fire. Please search for similar issues before posting your own, there's a good chance somebody else had the same issue you have now and had it resolved. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there to a fix.
|
|
||||||
|
|
||||||
* [Feedback and suggestions forum](http://gitlab.uservoice.com/forums/176466-general) is the place to propose and discuss new features for GitLab.
|
|
||||||
|
|
||||||
* [Support subscription](http://www.gitlab.com/subscription/) connect you to the knowledge of GitLab experts that will resolve your issues and answer your questions.
|
|
||||||
|
|
||||||
* [Consultancy](http://www.gitlab.com/consultancy/) allows you hire GitLab exports for installations, upgrades and customizations.
|
|
||||||
|
|
||||||
* [Contributing guide](https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md) describes how to submit pull requests and issues. Pull requests and issues not in line with the guidelines in this document will be closed without comment.
|
|
||||||
|
|
||||||
|
|
||||||
### Getting in touch
|
### Getting in touch
|
||||||
|
|
||||||
|
* [Contributing guide](https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md)
|
||||||
|
|
||||||
* [Core team](https://github.com/gitlabhq?tab=members)
|
* [Core team](https://github.com/gitlabhq?tab=members)
|
||||||
|
|
||||||
* [Contributors](https://github.com/gitlabhq/gitlabhq/graphs/contributors)
|
* [Contributors](https://github.com/gitlabhq/gitlabhq/graphs/contributors)
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
## GitLab Roadmap
|
## GitLab Roadmap
|
||||||
|
|
||||||
### v5.1 April 22
|
### v5.0 March 22
|
||||||
|
|
||||||
* Not decided yet.
|
* Replace gitolite with gitlab-shell
|
||||||
|
* Usability improvements
|
||||||
|
* Notification improvements
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
5.1.0pre
|
5.0.0
|
||||||
|
|
|
@ -3,11 +3,3 @@ $ ->
|
||||||
container = $(@).closest(".js-toggler-container")
|
container = $(@).closest(".js-toggler-container")
|
||||||
|
|
||||||
container.toggleClass("on")
|
container.toggleClass("on")
|
||||||
|
|
||||||
$("body").on "click", ".js-toggle-visibility-link", (e) ->
|
|
||||||
$(@).find('i').
|
|
||||||
toggleClass('icon-chevron-down').
|
|
||||||
toggleClass('icon-chevron-up')
|
|
||||||
container = $(".js-toggle-visibility-container")
|
|
||||||
container.toggleClass("hide")
|
|
||||||
e.preventDefault()
|
|
||||||
|
|
|
@ -5,10 +5,6 @@ class BranchGraph
|
||||||
@mspace = 0
|
@mspace = 0
|
||||||
@parents = {}
|
@parents = {}
|
||||||
@colors = ["#000"]
|
@colors = ["#000"]
|
||||||
@offsetX = 120
|
|
||||||
@offsetY = 20
|
|
||||||
@unitTime = 30
|
|
||||||
@unitSpace = 10
|
|
||||||
@load()
|
@load()
|
||||||
|
|
||||||
load: ->
|
load: ->
|
||||||
|
@ -24,6 +20,8 @@ class BranchGraph
|
||||||
|
|
||||||
prepareData: (@days, @commits) ->
|
prepareData: (@days, @commits) ->
|
||||||
@collectParents()
|
@collectParents()
|
||||||
|
@mtime += 4
|
||||||
|
@mspace += 10
|
||||||
|
|
||||||
for c in @commits
|
for c in @commits
|
||||||
c.isParent = true if c.id of @parents
|
c.isParent = true if c.id of @parents
|
||||||
|
@ -37,7 +35,6 @@ class BranchGraph
|
||||||
@mspace = Math.max(@mspace, c.space)
|
@mspace = Math.max(@mspace, c.space)
|
||||||
for p in c.parents
|
for p in c.parents
|
||||||
@parents[p[0]] = true
|
@parents[p[0]] = true
|
||||||
@mspace = Math.max(@mspace, p[1])
|
|
||||||
|
|
||||||
collectColors: ->
|
collectColors: ->
|
||||||
k = 0
|
k = 0
|
||||||
|
@ -49,23 +46,25 @@ class BranchGraph
|
||||||
k++
|
k++
|
||||||
|
|
||||||
buildGraph: ->
|
buildGraph: ->
|
||||||
graphHeight = $(@element).height()
|
|
||||||
graphWidth = $(@element).width()
|
graphWidth = $(@element).width()
|
||||||
ch = Math.max(graphHeight, @offsetY + @unitTime * @mtime + 150)
|
ch = @mspace * 20 + 100
|
||||||
cw = Math.max(graphWidth, @offsetX + @unitSpace * @mspace + 300)
|
cw = Math.max(graphWidth, @mtime * 20 + 260)
|
||||||
@r = r = Raphael(@element.get(0), cw, ch)
|
r = Raphael(@element.get(0), cw, ch)
|
||||||
top = r.set()
|
top = r.set()
|
||||||
cuday = 0
|
cuday = 0
|
||||||
cumonth = ""
|
cumonth = ""
|
||||||
barHeight = Math.max(graphHeight, @unitTime * @days.length + 320)
|
offsetX = 20
|
||||||
|
offsetY = 60
|
||||||
r.rect(0, 0, 26, barHeight).attr fill: "#222"
|
barWidth = Math.max(graphWidth, @days.length * 20 + 320)
|
||||||
r.rect(26, 0, 20, barHeight).attr fill: "#444"
|
scrollLeft = cw
|
||||||
|
@raphael = r
|
||||||
|
r.rect(0, 0, barWidth, 20).attr fill: "#222"
|
||||||
|
r.rect(0, 20, barWidth, 20).attr fill: "#444"
|
||||||
|
|
||||||
for day, mm in @days
|
for day, mm in @days
|
||||||
if cuday isnt day[0]
|
if cuday isnt day[0]
|
||||||
# Dates
|
# Dates
|
||||||
r.text(36, @offsetY + @unitTime * mm, day[0])
|
r.text(offsetX + mm * 20, 31, day[0])
|
||||||
.attr(
|
.attr(
|
||||||
font: "12px Monaco, monospace"
|
font: "12px Monaco, monospace"
|
||||||
fill: "#DDD"
|
fill: "#DDD"
|
||||||
|
@ -74,7 +73,7 @@ class BranchGraph
|
||||||
|
|
||||||
if cumonth isnt day[1]
|
if cumonth isnt day[1]
|
||||||
# Months
|
# Months
|
||||||
r.text(13, @offsetY + @unitTime * mm, day[1])
|
r.text(offsetX + mm * 20, 11, day[1])
|
||||||
.attr(
|
.attr(
|
||||||
font: "12px Monaco, monospace"
|
font: "12px Monaco, monospace"
|
||||||
fill: "#EEE"
|
fill: "#EEE"
|
||||||
|
@ -82,20 +81,61 @@ class BranchGraph
|
||||||
cumonth = day[1]
|
cumonth = day[1]
|
||||||
|
|
||||||
for commit in @commits
|
for commit in @commits
|
||||||
x = @offsetX + @unitSpace * (@mspace - commit.space)
|
x = offsetX + 20 * commit.time
|
||||||
y = @offsetY + @unitTime * commit.time
|
y = offsetY + 10 * commit.space
|
||||||
|
# Draw dot
|
||||||
|
r.circle(x, y, 3).attr(
|
||||||
|
fill: @colors[commit.space]
|
||||||
|
stroke: "none"
|
||||||
|
)
|
||||||
|
|
||||||
@drawDot(x, y, commit)
|
# Draw lines
|
||||||
|
for parent in commit.parents
|
||||||
|
parentCommit = @preparedCommits[parent[0]]
|
||||||
|
parentX = offsetX + 20 * parentCommit.time
|
||||||
|
parentY1 = offsetY + 10 * parentCommit.space
|
||||||
|
parentY2 = offsetY + 10 * parent[1]
|
||||||
|
if parentCommit.space is commit.space and parentCommit.space is parent[1]
|
||||||
|
r.path(["M", x, y, "L", parentX, parentY1]).attr(
|
||||||
|
stroke: @colors[parentCommit.space]
|
||||||
|
"stroke-width": 2
|
||||||
|
)
|
||||||
|
|
||||||
@drawLines(x, y, commit)
|
else if parentCommit.space < commit.space
|
||||||
|
if y is parentY2
|
||||||
|
r.path(["M", x - 5, y, "l-5,-2,0,4,5,-2", "L", x - 10, y, "L", x - 15, parentY2, "L", parentX + 5, parentY2, "L", parentX, parentY1]).attr(
|
||||||
|
stroke: @colors[commit.space]
|
||||||
|
"stroke-width": 2
|
||||||
|
)
|
||||||
|
|
||||||
@appendLabel(x, y, commit.refs) if commit.refs
|
else
|
||||||
|
r.path(["M", x - 3, y - 6, "l-4,-3,4,-2,0,5", "L", x - 5, y - 10, "L", x - 10, parentY2, "L", parentX + 5, parentY2, "L", parentX, parentY1]).attr(
|
||||||
|
stroke: @colors[commit.space]
|
||||||
|
"stroke-width": 2
|
||||||
|
)
|
||||||
|
|
||||||
@appendAnchor(top, commit, x, y)
|
else
|
||||||
|
r.path(["M", x - 3, y + 6, "l-4,3,4,2,0,-5", "L", x - 5, y + 10, "L", x - 10, parentY2, "L", parentX + 5, parentY2, "L", parentX, parentY1]).attr(
|
||||||
|
stroke: @colors[parentCommit.space]
|
||||||
|
"stroke-width": 2
|
||||||
|
)
|
||||||
|
|
||||||
@markCommit(x, y, commit, graphHeight)
|
@appendLabel x, y, commit.refs if commit.refs
|
||||||
|
|
||||||
|
# Mark commit and displayed in the center
|
||||||
|
if commit.id is @options.commit_id
|
||||||
|
r.path(["M", x, y - 5, "L", x + 4, y - 15, "L", x - 4, y - 15, "Z"]).attr(
|
||||||
|
fill: "#000"
|
||||||
|
"fill-opacity": .7
|
||||||
|
stroke: "none"
|
||||||
|
)
|
||||||
|
|
||||||
|
scrollLeft = x - graphWidth / 2
|
||||||
|
|
||||||
|
@appendAnchor top, commit, x, y
|
||||||
|
|
||||||
top.toFront()
|
top.toFront()
|
||||||
|
@element.scrollLeft scrollLeft
|
||||||
@bindEvents()
|
@bindEvents()
|
||||||
|
|
||||||
bindEvents: ->
|
bindEvents: ->
|
||||||
|
@ -127,37 +167,35 @@ class BranchGraph
|
||||||
element.scrollTop element.scrollTop() + 50 if event.keyCode is 40
|
element.scrollTop element.scrollTop() + 50 if event.keyCode is 40
|
||||||
|
|
||||||
appendLabel: (x, y, refs) ->
|
appendLabel: (x, y, refs) ->
|
||||||
r = @r
|
r = @raphael
|
||||||
shortrefs = refs
|
shortrefs = refs
|
||||||
# Truncate if longer than 15 chars
|
# Truncate if longer than 15 chars
|
||||||
shortrefs = shortrefs.substr(0, 15) + "…" if shortrefs.length > 17
|
shortrefs = shortrefs.substr(0, 15) + "…" if shortrefs.length > 17
|
||||||
text = r.text(x + 4, y, shortrefs).attr(
|
text = r.text(x + 5, y + 8 + 10, shortrefs).attr(
|
||||||
"text-anchor": "start"
|
|
||||||
font: "10px Monaco, monospace"
|
font: "10px Monaco, monospace"
|
||||||
fill: "#FFF"
|
fill: "#FFF"
|
||||||
title: refs
|
title: refs
|
||||||
)
|
)
|
||||||
textbox = text.getBBox()
|
textbox = text.getBBox()
|
||||||
|
text.transform ["t", textbox.height / -4, textbox.width / 2 + 5, "r90"]
|
||||||
# Create rectangle based on the size of the textbox
|
# Create rectangle based on the size of the textbox
|
||||||
rect = r.rect(x, y - 7, textbox.width + 5, textbox.height + 5, 4).attr(
|
rect = r.rect(x, y, textbox.width + 15, textbox.height + 5, 4).attr(
|
||||||
fill: "#000"
|
fill: "#000"
|
||||||
"fill-opacity": .5
|
"fill-opacity": .7
|
||||||
stroke: "none"
|
stroke: "none"
|
||||||
)
|
)
|
||||||
triangle = r.path(["M", x - 5, y, "L", x - 15, y - 4, "L", x - 15, y + 4, "Z"]).attr(
|
triangle = r.path(["M", x, y + 5, "L", x + 4, y + 15, "L", x - 4, y + 15, "Z"]).attr(
|
||||||
fill: "#000"
|
fill: "#000"
|
||||||
"fill-opacity": .5
|
"fill-opacity": .7
|
||||||
stroke: "none"
|
stroke: "none"
|
||||||
)
|
)
|
||||||
|
# Rotate and reposition rectangle over text
|
||||||
label = r.set(rect, text)
|
rect.transform ["r", 90, x, y, "t", 15, -9]
|
||||||
label.transform(["t", -rect.getBBox().width - 15, 0])
|
|
||||||
|
|
||||||
# Set text to front
|
# Set text to front
|
||||||
text.toFront()
|
text.toFront()
|
||||||
|
|
||||||
appendAnchor: (top, commit, x, y) ->
|
appendAnchor: (top, commit, x, y) ->
|
||||||
r = @r
|
r = @raphael
|
||||||
options = @options
|
options = @options
|
||||||
anchor = r.circle(x, y, 10).attr(
|
anchor = r.circle(x, y, 10).attr(
|
||||||
fill: "#000"
|
fill: "#000"
|
||||||
|
@ -166,92 +204,18 @@ class BranchGraph
|
||||||
).click(->
|
).click(->
|
||||||
window.open options.commit_url.replace("%s", commit.id), "_blank"
|
window.open options.commit_url.replace("%s", commit.id), "_blank"
|
||||||
).hover(->
|
).hover(->
|
||||||
@tooltip = r.commitTooltip(x + 5, y, commit)
|
@tooltip = r.commitTooltip(x, y + 5, commit)
|
||||||
top.push @tooltip.insertBefore(this)
|
top.push @tooltip.insertBefore(this)
|
||||||
, ->
|
, ->
|
||||||
@tooltip and @tooltip.remove() and delete @tooltip
|
@tooltip and @tooltip.remove() and delete @tooltip
|
||||||
)
|
)
|
||||||
top.push anchor
|
top.push anchor
|
||||||
|
|
||||||
drawDot: (x, y, commit) ->
|
|
||||||
r = @r
|
|
||||||
r.circle(x, y, 3).attr(
|
|
||||||
fill: @colors[commit.space]
|
|
||||||
stroke: "none"
|
|
||||||
)
|
|
||||||
r.rect(@offsetX + @unitSpace * @mspace + 10, y - 10, 20, 20).attr(
|
|
||||||
fill: "url(#{commit.author.icon})"
|
|
||||||
stroke: @colors[commit.space]
|
|
||||||
"stroke-width": 2
|
|
||||||
)
|
|
||||||
r.text(@offsetX + @unitSpace * @mspace + 35, y, commit.message.split("\n")[0]).attr(
|
|
||||||
"text-anchor": "start"
|
|
||||||
font: "14px Monaco, monospace"
|
|
||||||
)
|
|
||||||
|
|
||||||
drawLines: (x, y, commit) ->
|
|
||||||
r = @r
|
|
||||||
for parent, i in commit.parents
|
|
||||||
parentCommit = @preparedCommits[parent[0]]
|
|
||||||
parentY = @offsetY + @unitTime * parentCommit.time
|
|
||||||
parentX1 = @offsetX + @unitSpace * (@mspace - parentCommit.space)
|
|
||||||
parentX2 = @offsetX + @unitSpace * (@mspace - parent[1])
|
|
||||||
|
|
||||||
# Set line color
|
|
||||||
if parentCommit.space <= commit.space
|
|
||||||
color = @colors[commit.space]
|
|
||||||
|
|
||||||
else
|
|
||||||
color = @colors[parentCommit.space]
|
|
||||||
|
|
||||||
# Build line shape
|
|
||||||
if parent[1] is commit.space
|
|
||||||
offset = [0, 5]
|
|
||||||
arrow = "l-2,5,4,0,-2,-5,0,5"
|
|
||||||
|
|
||||||
else if parent[1] < commit.space
|
|
||||||
offset = [3, 3]
|
|
||||||
arrow = "l5,0,-2,4,-3,-4,4,2"
|
|
||||||
|
|
||||||
else
|
|
||||||
offset = [-3, 3]
|
|
||||||
arrow = "l-5,0,2,4,3,-4,-4,2"
|
|
||||||
|
|
||||||
# Start point
|
|
||||||
route = ["M", x + offset[0], y + offset[1]]
|
|
||||||
|
|
||||||
# Add arrow if not first parent
|
|
||||||
if i > 0
|
|
||||||
route.push(arrow)
|
|
||||||
|
|
||||||
# Circumvent if overlap
|
|
||||||
if commit.space isnt parentCommit.space or commit.space isnt parent[1]
|
|
||||||
route.push(
|
|
||||||
"L", parentX2, y + 10,
|
|
||||||
"L", parentX2, parentY - 5,
|
|
||||||
)
|
|
||||||
|
|
||||||
# End point
|
|
||||||
route.push("L", parentX1, parentY)
|
|
||||||
|
|
||||||
r
|
|
||||||
.path(route)
|
|
||||||
.attr(
|
|
||||||
stroke: color
|
|
||||||
"stroke-width": 2)
|
|
||||||
|
|
||||||
markCommit: (x, y, commit, graphHeight) ->
|
|
||||||
if commit.id is @options.commit_id
|
|
||||||
r = @r
|
|
||||||
r.path(["M", x + 5, y, "L", x + 15, y + 4, "L", x + 15, y - 4, "Z"]).attr(
|
|
||||||
fill: "#000"
|
|
||||||
"fill-opacity": .5
|
|
||||||
stroke: "none"
|
|
||||||
)
|
|
||||||
# Displayed in the center
|
|
||||||
@element.scrollTop(y - graphHeight / 2)
|
|
||||||
|
|
||||||
Raphael::commitTooltip = (x, y, commit) ->
|
Raphael::commitTooltip = (x, y, commit) ->
|
||||||
|
icon = undefined
|
||||||
|
nameText = undefined
|
||||||
|
idText = undefined
|
||||||
|
messageText = undefined
|
||||||
boxWidth = 300
|
boxWidth = 300
|
||||||
boxHeight = 200
|
boxHeight = 200
|
||||||
icon = @image(commit.author.icon, x, y, 20, 20)
|
icon = @image(commit.author.icon, x, y, 20, 20)
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
$.fn.showAndHide = ->
|
|
||||||
$(@).show().
|
|
||||||
delay(3000).
|
|
||||||
fadeOut()
|
|
||||||
|
|
||||||
$.fn.enableButton = ->
|
|
||||||
$(@).removeAttr('disabled').
|
|
||||||
removeClass('disabled')
|
|
||||||
|
|
|
@ -7,8 +7,6 @@ window.slugify = (text) ->
|
||||||
window.ajaxGet = (url) ->
|
window.ajaxGet = (url) ->
|
||||||
$.ajax({type: "GET", url: url, dataType: "script"})
|
$.ajax({type: "GET", url: url, dataType: "script"})
|
||||||
|
|
||||||
window.showAndHide = (selector) ->
|
|
||||||
|
|
||||||
window.errorMessage = (message) ->
|
window.errorMessage = (message) ->
|
||||||
ehtml = $("<p>")
|
ehtml = $("<p>")
|
||||||
ehtml.addClass("error_message")
|
ehtml.addClass("error_message")
|
||||||
|
|
|
@ -15,8 +15,6 @@ $ ->
|
||||||
$(this).find('.update-failed').hide()
|
$(this).find('.update-failed').hide()
|
||||||
|
|
||||||
$('.update-username form').on 'ajax:complete', ->
|
$('.update-username form').on 'ajax:complete', ->
|
||||||
$(this).find('.btn-save').enableButton()
|
$(this).find('.save-btn').removeAttr('disabled')
|
||||||
|
$(this).find('.save-btn').removeClass('disabled')
|
||||||
$(this).find('.loading-gif').hide()
|
$(this).find('.loading-gif').hide()
|
||||||
|
|
||||||
$('.update-notifications').on 'ajax:complete', ->
|
|
||||||
$(this).find('.btn-save').enableButton()
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ $ ->
|
||||||
$('span.log_loading:first').removeClass('hide')
|
$('span.log_loading:first').removeClass('hide')
|
||||||
|
|
||||||
$('#tree-slider .tree-item-file-name a, .breadcrumb li > a').live "click", ->
|
$('#tree-slider .tree-item-file-name a, .breadcrumb li > a').live "click", ->
|
||||||
$("#tree-content-holder").hide("slide", { direction: "left" }, 400)
|
$("#tree-content-holder").hide("slide", { direction: "left" }, 150)
|
||||||
|
|
||||||
# Make the entire tree-item row clickable, but not if clicking another link (like a commit message)
|
# Make the entire tree-item row clickable, but not if clicking another link (like a commit message)
|
||||||
$("#tree-slider .tree-item").live 'click', (e) ->
|
$("#tree-slider .tree-item").live 'click', (e) ->
|
||||||
|
|
|
@ -58,26 +58,14 @@
|
||||||
form.show()
|
form.show()
|
||||||
|
|
||||||
renderNote: (note) ->
|
renderNote: (note) ->
|
||||||
template = Wall.noteTemplate()
|
author = '<strong class="wall-author">' + note.author.name + '</strong>'
|
||||||
template = template.replace('{{author_name}}', note.author.name)
|
body = '<span class="wall-text">' + linkify(sanitize(note.body)) + '</span>'
|
||||||
template = template.replace('{{created_at}}', note.created_at)
|
file = ''
|
||||||
template = template.replace('{{text}}', linkify(sanitize(note.body)))
|
time = '<abbr class="timeago" title="' + note.created_at + '">' + note.created_at + '</time>'
|
||||||
|
|
||||||
if note.attachment
|
if note.attachment
|
||||||
file = '<i class="icon-paper-clip"/><a href="/files/note/' + note.id + '/' + note.attachment + '">' + note.attachment + '</a>'
|
file = '<span class="wall-file"><a href="/files/note/' + note.id + '/' + note.attachment + '">' + note.attachment + '</a></span>'
|
||||||
else
|
|
||||||
file = ''
|
|
||||||
template = template.replace('{{file}}', file)
|
|
||||||
|
|
||||||
|
html = '<li>' + author + body + file + time + '</li>'
|
||||||
|
|
||||||
$('ul.notes').append(template)
|
$('ul.notes').append(html)
|
||||||
|
|
||||||
noteTemplate: ->
|
|
||||||
return '<li>
|
|
||||||
<strong class="wall-author">{{author_name}}</strong>
|
|
||||||
<span class="wall-text">
|
|
||||||
{{text}}
|
|
||||||
<span class="wall-file">{{file}}</span>
|
|
||||||
</span>
|
|
||||||
<abbr class="timeago" title="{{created_at}}">{{created_at}}</abbr>
|
|
||||||
</li>'
|
|
||||||
|
|
|
@ -100,9 +100,8 @@
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn {
|
.btn-tiny {
|
||||||
position: relative;
|
@include box-shadow(0 0px 0px 1px #f1f1f1);
|
||||||
top: -2px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-pills {
|
.nav-pills {
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
.graph {
|
.graph {
|
||||||
background: #f1f1f1;
|
background: #f1f1f1;
|
||||||
cursor: move;
|
cursor: move;
|
||||||
height: 500px;
|
height: 70%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
.main-nav {
|
/*
|
||||||
|
* Main Menu of Application
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
ul.main_menu {
|
||||||
|
margin: auto;
|
||||||
margin: 30px 0;
|
margin: 30px 0;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
border-bottom: 1px solid #E1E1E1;
|
height: 38px;
|
||||||
|
|
||||||
ul {
|
|
||||||
margin: auto;
|
|
||||||
height: 39px;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 3px;
|
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
.count {
|
.count {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -32,11 +32,11 @@
|
||||||
margin: 0;
|
margin: 0;
|
||||||
display: table-cell;
|
display: table-cell;
|
||||||
width: 1%;
|
width: 1%;
|
||||||
|
border-bottom: 2px solid #EEE;
|
||||||
&.active {
|
&.active {
|
||||||
border-bottom: 3px solid #777;
|
border-bottom: 2px solid #474D57;
|
||||||
a {
|
a {
|
||||||
color: $style_color;
|
color: $style_color;
|
||||||
font-weight: bolder;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,10 +55,14 @@
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-weight: normal;
|
font-weight: normal;
|
||||||
height: 36px;
|
height: 36px;
|
||||||
line-height: 34px;
|
line-height: 36px;
|
||||||
color: #777;
|
color: #777;
|
||||||
text-shadow: 0 1px 1px white;
|
text-shadow: 0 1px 1px white;
|
||||||
padding: 0 10px;
|
padding: 0 10px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
/*
|
||||||
|
* End of Main Menu
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
|
@ -274,15 +274,6 @@ ul.notes {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.common-note-form {
|
|
||||||
margin: 0;
|
|
||||||
height: 140px;
|
|
||||||
background: #F9F9F9;
|
|
||||||
padding: 3px;
|
|
||||||
padding-bottom: 25px;
|
|
||||||
border: 1px solid #DDD;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.note-form-actions {
|
.note-form-actions {
|
||||||
background: #F9F9F9;
|
background: #F9F9F9;
|
||||||
|
@ -290,8 +281,8 @@ ul.notes {
|
||||||
padding: 0 5px;
|
padding: 0 5px;
|
||||||
|
|
||||||
.note-form-option {
|
.note-form-option {
|
||||||
margin-top: 10px;
|
margin-top: 8px;
|
||||||
margin-left: 30px;
|
margin-left: 15px;
|
||||||
@extend .pull-left;
|
@extend .pull-left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,31 +14,12 @@
|
||||||
|
|
||||||
.notes {
|
.notes {
|
||||||
margin-bottom: 160px;
|
margin-bottom: 160px;
|
||||||
background: #FFE;
|
|
||||||
border: 1px solid #EED;
|
|
||||||
|
|
||||||
> li {
|
|
||||||
@extend .clearfix;
|
|
||||||
border-bottom: 1px solid #EED;
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wall-author {
|
.wall-author {
|
||||||
color: #666;
|
color: #666;
|
||||||
float: left;
|
margin-right: 10px;
|
||||||
font-size: 12px;
|
border-right: 1px solid #CCC;
|
||||||
width: 120px;
|
padding-right: 5px
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.wall-text {
|
|
||||||
border-left: 1px solid #CCC;
|
|
||||||
margin-left: 10px;
|
|
||||||
padding-left: 10px;
|
|
||||||
float: left;
|
|
||||||
width: 75%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.wall-file {
|
.wall-file {
|
||||||
|
|
|
@ -12,6 +12,7 @@ class CommitLoadContext < BaseContext
|
||||||
commit = project.repository.commit(params[:id])
|
commit = project.repository.commit(params[:id])
|
||||||
|
|
||||||
if commit
|
if commit
|
||||||
|
commit = CommitDecorator.decorate(commit)
|
||||||
line_notes = project.notes.for_commit_id(commit.id).inline
|
line_notes = project.notes.for_commit_id(commit.id).inline
|
||||||
|
|
||||||
result[:commit] = commit
|
result[:commit] = commit
|
||||||
|
|
|
@ -3,6 +3,8 @@ module Notes
|
||||||
def execute
|
def execute
|
||||||
note = project.notes.new(params[:note])
|
note = project.notes.new(params[:note])
|
||||||
note.author = current_user
|
note.author = current_user
|
||||||
|
note.notify = params[:notify].present?
|
||||||
|
note.notify_author = params[:notify_author].present?
|
||||||
note.save
|
note.save
|
||||||
note
|
note
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
module Projects
|
|
||||||
class TransferContext < BaseContext
|
|
||||||
def execute(role = :default)
|
|
||||||
namespace_id = params[:project].delete(:namespace_id)
|
|
||||||
allowed_transfer = can?(current_user, :change_namespace, project) || role == :admin
|
|
||||||
|
|
||||||
if allowed_transfer && namespace_id.present?
|
|
||||||
if namespace_id == Namespace.global_id
|
|
||||||
if project.namespace.present?
|
|
||||||
# Transfer to global namespace from anyone
|
|
||||||
project.transfer(nil)
|
|
||||||
end
|
|
||||||
elsif namespace_id.to_i != project.namespace_id
|
|
||||||
# Transfer to someone namespace
|
|
||||||
namespace = Namespace.find(namespace_id)
|
|
||||||
project.transfer(namespace)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
rescue ProjectTransferService::TransferError => ex
|
|
||||||
project.reload
|
|
||||||
project.errors.add(:namespace_id, ex.message)
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
|
@ -1,8 +1,24 @@
|
||||||
module Projects
|
module Projects
|
||||||
class UpdateContext < BaseContext
|
class UpdateContext < BaseContext
|
||||||
def execute(role = :default)
|
def execute(role = :default)
|
||||||
params[:project].delete(:namespace_id)
|
namespace_id = params[:project].delete(:namespace_id)
|
||||||
params[:project].delete(:public) unless can?(current_user, :change_public_mode, project)
|
params[:project].delete(:public) unless can?(current_user, :change_public_mode, project)
|
||||||
|
|
||||||
|
allowed_transfer = can?(current_user, :change_namespace, project) || role == :admin
|
||||||
|
|
||||||
|
if allowed_transfer && namespace_id.present?
|
||||||
|
if namespace_id == Namespace.global_id
|
||||||
|
if project.namespace.present?
|
||||||
|
# Transfer to global namespace from anyone
|
||||||
|
project.transfer(nil)
|
||||||
|
end
|
||||||
|
elsif namespace_id.to_i != project.namespace_id
|
||||||
|
# Transfer to someone namespace
|
||||||
|
namespace = Namespace.find(namespace_id)
|
||||||
|
project.transfer(namespace)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
project.update_attributes(params[:project], as: role)
|
project.update_attributes(params[:project], as: role)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -19,6 +19,34 @@ class Admin::ProjectsController < Admin::ApplicationController
|
||||||
@users = @users.all
|
@users = @users.all
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def edit
|
||||||
|
end
|
||||||
|
|
||||||
|
def team_update
|
||||||
|
@project.team.add_users_ids(params[:user_ids], params[:project_access])
|
||||||
|
|
||||||
|
redirect_to [:admin, @project], notice: 'Project was successfully updated.'
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
project.creator = current_user unless project.creator
|
||||||
|
|
||||||
|
status = ::Projects::UpdateContext.new(project, current_user, params).execute(:admin)
|
||||||
|
|
||||||
|
if status
|
||||||
|
redirect_to [:admin, @project], notice: 'Project was successfully updated.'
|
||||||
|
else
|
||||||
|
render action: "edit"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
@project.team.truncate
|
||||||
|
@project.destroy
|
||||||
|
|
||||||
|
redirect_to admin_projects_path, notice: 'Project was successfully deleted.'
|
||||||
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def project
|
def project
|
||||||
|
|
|
@ -84,8 +84,6 @@ class Admin::UsersController < Admin::ApplicationController
|
||||||
format.html { redirect_to [:admin, admin_user], notice: 'User was successfully updated.' }
|
format.html { redirect_to [:admin, admin_user], notice: 'User was successfully updated.' }
|
||||||
format.json { head :ok }
|
format.json { head :ok }
|
||||||
else
|
else
|
||||||
# restore username to keep form action url.
|
|
||||||
admin_user.username = params[:id]
|
|
||||||
format.html { render action: "edit" }
|
format.html { render action: "edit" }
|
||||||
format.json { render json: admin_user.errors, status: :unprocessable_entity }
|
format.json { render json: admin_user.errors, status: :unprocessable_entity }
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,7 +7,10 @@ class BlameController < ProjectResourceController
|
||||||
before_filter :authorize_code_access!
|
before_filter :authorize_code_access!
|
||||||
before_filter :require_non_empty_project
|
before_filter :require_non_empty_project
|
||||||
|
|
||||||
|
before_filter :assign_ref_vars
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@blame = Gitlab::Git::Blame.new(project.repository, @commit.id, @path)
|
@repo = @project.repo
|
||||||
|
@blame = Grit::Blob.blame(@repo, @commit.id, @path)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -7,6 +7,8 @@ class BlobController < ProjectResourceController
|
||||||
before_filter :authorize_code_access!
|
before_filter :authorize_code_access!
|
||||||
before_filter :require_non_empty_project
|
before_filter :require_non_empty_project
|
||||||
|
|
||||||
|
before_filter :assign_ref_vars
|
||||||
|
|
||||||
def show
|
def show
|
||||||
if @tree.is_blob?
|
if @tree.is_blob?
|
||||||
send_data(
|
send_data(
|
||||||
|
|
|
@ -13,6 +13,7 @@ class CommitsController < ProjectResourceController
|
||||||
@limit, @offset = (params[:limit] || 40), (params[:offset] || 0)
|
@limit, @offset = (params[:limit] || 40), (params[:offset] || 0)
|
||||||
|
|
||||||
@commits = @repo.commits(@ref, @path, @limit, @offset)
|
@commits = @repo.commits(@ref, @path, @limit, @offset)
|
||||||
|
@commits = CommitDecorator.decorate_collection(@commits)
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html # index.html.erb
|
format.html # index.html.erb
|
||||||
|
|
|
@ -8,13 +8,15 @@ class CompareController < ProjectResourceController
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
compare = Gitlab::Git::Compare.new(project.repository, params[:from], params[:to])
|
result = Commit.compare(project, params[:from], params[:to])
|
||||||
|
|
||||||
@commits = compare.commits
|
@commits = result[:commits]
|
||||||
@commit = compare.commit
|
@commit = result[:commit]
|
||||||
@diffs = compare.diffs
|
@diffs = result[:diffs]
|
||||||
@refs_are_same = compare.same
|
@refs_are_same = result[:same]
|
||||||
@line_notes = []
|
@line_notes = []
|
||||||
|
|
||||||
|
@commits = CommitDecorator.decorate_collection(@commits)
|
||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
# Controller for edit a repository's file
|
|
||||||
class EditTreeController < ProjectResourceController
|
|
||||||
include ExtractsPath
|
|
||||||
|
|
||||||
# Authorize
|
|
||||||
before_filter :authorize_read_project!
|
|
||||||
before_filter :authorize_code_access!
|
|
||||||
before_filter :require_non_empty_project
|
|
||||||
|
|
||||||
before_filter :edit_requirements, only: [:edit, :update]
|
|
||||||
|
|
||||||
def show
|
|
||||||
@last_commit = @project.repository.last_commit_for(@ref, @path).sha
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
|
||||||
edit_file_action = Gitlab::Satellite::EditFileAction.new(current_user, @project, @ref, @path)
|
|
||||||
updated_successfully = edit_file_action.commit!(
|
|
||||||
params[:content],
|
|
||||||
params[:commit_message],
|
|
||||||
params[:last_commit]
|
|
||||||
)
|
|
||||||
|
|
||||||
if updated_successfully
|
|
||||||
redirect_to project_tree_path(@project, @id), notice: "Your changes have been successfully commited"
|
|
||||||
else
|
|
||||||
flash[:notice] = "Your changes could not be commited, because the file has been changed"
|
|
||||||
render :edit
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def edit_requirements
|
|
||||||
unless @tree.is_blob? && @tree.text?
|
|
||||||
redirect_to project_tree_path(@project, @id), notice: "You can only edit text files"
|
|
||||||
end
|
|
||||||
|
|
||||||
allowed = if project.protected_branch? @ref
|
|
||||||
can?(current_user, :push_code_to_protected_branches, project)
|
|
||||||
else
|
|
||||||
can?(current_user, :push_code, project)
|
|
||||||
end
|
|
||||||
|
|
||||||
return access_denied! unless allowed
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -94,10 +94,12 @@ class MergeRequestsController < ProjectResourceController
|
||||||
|
|
||||||
def branch_from
|
def branch_from
|
||||||
@commit = @repository.commit(params[:ref])
|
@commit = @repository.commit(params[:ref])
|
||||||
|
@commit = CommitDecorator.decorate(@commit)
|
||||||
end
|
end
|
||||||
|
|
||||||
def branch_to
|
def branch_to
|
||||||
@commit = @repository.commit(params[:ref])
|
@commit = @repository.commit(params[:ref])
|
||||||
|
@commit = CommitDecorator.decorate(@commit)
|
||||||
end
|
end
|
||||||
|
|
||||||
def ci_status
|
def ci_status
|
||||||
|
@ -127,11 +129,11 @@ class MergeRequestsController < ProjectResourceController
|
||||||
|
|
||||||
def validates_merge_request
|
def validates_merge_request
|
||||||
# Show git not found page if target branch doesn't exist
|
# Show git not found page if target branch doesn't exist
|
||||||
return invalid_mr unless @project.repository.branch_names.include?(@merge_request.target_branch)
|
return invalid_mr unless @project.repo.heads.map(&:name).include?(@merge_request.target_branch)
|
||||||
|
|
||||||
# Show git not found page if source branch doesn't exist
|
# Show git not found page if source branch doesn't exist
|
||||||
# and there is no saved commits between source & target branch
|
# and there is no saved commits between source & target branch
|
||||||
return invalid_mr if !@project.repository.branch_names.include?(@merge_request.source_branch) && @merge_request.commits.blank?
|
return invalid_mr if !@project.repo.heads.map(&:name).include?(@merge_request.source_branch) && @merge_request.commits.blank?
|
||||||
end
|
end
|
||||||
|
|
||||||
def define_show_vars
|
def define_show_vars
|
||||||
|
@ -141,6 +143,7 @@ class MergeRequestsController < ProjectResourceController
|
||||||
# Get commits from repository
|
# Get commits from repository
|
||||||
# or from cache if already merged
|
# or from cache if already merged
|
||||||
@commits = @merge_request.commits
|
@commits = @merge_request.commits
|
||||||
|
@commits = CommitDecorator.decorate_collection(@commits)
|
||||||
|
|
||||||
@allowed_to_merge = allowed_to_merge?
|
@allowed_to_merge = allowed_to_merge?
|
||||||
@show_merge_controls = @merge_request.opened? && @commits.any? && @allowed_to_merge
|
@show_merge_controls = @merge_request.opened? && @commits.any? && @allowed_to_merge
|
||||||
|
|
|
@ -1,13 +0,0 @@
|
||||||
class NotificationsController < ApplicationController
|
|
||||||
layout 'profile'
|
|
||||||
|
|
||||||
def show
|
|
||||||
@notification = current_user.notification
|
|
||||||
@projects = current_user.authorized_projects
|
|
||||||
end
|
|
||||||
|
|
||||||
def update
|
|
||||||
current_user.notification_level = params[:notification_level]
|
|
||||||
@saved = current_user.save
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -4,7 +4,7 @@ class ProjectsController < ProjectResourceController
|
||||||
|
|
||||||
# Authorize
|
# Authorize
|
||||||
before_filter :authorize_read_project!, except: [:index, :new, :create]
|
before_filter :authorize_read_project!, except: [:index, :new, :create]
|
||||||
before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer]
|
before_filter :authorize_admin_project!, only: [:edit, :update, :destroy]
|
||||||
before_filter :require_non_empty_project, only: [:blob, :tree, :graph]
|
before_filter :require_non_empty_project, only: [:blob, :tree, :graph]
|
||||||
|
|
||||||
layout 'application', only: [:new, :create]
|
layout 'application', only: [:new, :create]
|
||||||
|
@ -45,10 +45,10 @@ class ProjectsController < ProjectResourceController
|
||||||
format.js
|
format.js
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
def transfer
|
rescue Project::TransferError => ex
|
||||||
::Projects::TransferContext.new(project, current_user, params).execute
|
@error = ex
|
||||||
|
render :update_failed
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
|
@ -57,11 +57,11 @@ class ProjectsController < ProjectResourceController
|
||||||
|
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.html do
|
format.html do
|
||||||
if @project.empty_repo?
|
if @project.repository && !@project.repository.empty?
|
||||||
render "projects/empty"
|
|
||||||
else
|
|
||||||
@last_push = current_user.recent_push(@project.id)
|
@last_push = current_user.recent_push(@project.id)
|
||||||
render :show
|
render :show
|
||||||
|
else
|
||||||
|
render "projects/empty"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
format.js
|
format.js
|
||||||
|
|
|
@ -34,6 +34,7 @@ class RefsController < ProjectResourceController
|
||||||
@logs = contents.map do |content|
|
@logs = contents.map do |content|
|
||||||
file = params[:path] ? File.join(params[:path], content.name) : content.name
|
file = params[:path] ? File.join(params[:path], content.name) : content.name
|
||||||
last_commit = @repo.commits(@commit.id, file, 1).last
|
last_commit = @repo.commits(@commit.id, file, 1).last
|
||||||
|
last_commit = CommitDecorator.decorate(last_commit)
|
||||||
{
|
{
|
||||||
file_name: content.name,
|
file_name: content.name,
|
||||||
commit: last_commit
|
commit: last_commit
|
||||||
|
@ -48,7 +49,9 @@ class RefsController < ProjectResourceController
|
||||||
|
|
||||||
@repo = project.repository
|
@repo = project.repository
|
||||||
@commit = @repo.commit(@ref)
|
@commit = @repo.commit(@ref)
|
||||||
|
@commit = CommitDecorator.decorate(@commit)
|
||||||
@tree = Tree.new(@commit.tree, @ref, params[:path])
|
@tree = Tree.new(@commit.tree, @ref, params[:path])
|
||||||
|
@tree = TreeDecorator.new(@tree)
|
||||||
@hex_path = Digest::SHA1.hexdigest(params[:path] || "")
|
@hex_path = Digest::SHA1.hexdigest(params[:path] || "")
|
||||||
|
|
||||||
if params[:path]
|
if params[:path]
|
||||||
|
|
|
@ -7,6 +7,9 @@ class TreeController < ProjectResourceController
|
||||||
before_filter :authorize_code_access!
|
before_filter :authorize_code_access!
|
||||||
before_filter :require_non_empty_project
|
before_filter :require_non_empty_project
|
||||||
|
|
||||||
|
before_filter :assign_ref_vars
|
||||||
|
before_filter :edit_requirements, only: [:edit, :update]
|
||||||
|
|
||||||
def show
|
def show
|
||||||
@hex_path = Digest::SHA1.hexdigest(@path)
|
@hex_path = Digest::SHA1.hexdigest(@path)
|
||||||
@logs_path = logs_file_project_ref_path(@project, @ref, @path)
|
@logs_path = logs_file_project_ref_path(@project, @ref, @path)
|
||||||
|
@ -17,4 +20,40 @@ class TreeController < ProjectResourceController
|
||||||
format.js { no_cache_headers }
|
format.js { no_cache_headers }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def edit
|
||||||
|
@last_commit = @project.repository.last_commit_for(@ref, @path).sha
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
edit_file_action = Gitlab::Satellite::EditFileAction.new(current_user, @project, @ref, @path)
|
||||||
|
updated_successfully = edit_file_action.commit!(
|
||||||
|
params[:content],
|
||||||
|
params[:commit_message],
|
||||||
|
params[:last_commit]
|
||||||
|
)
|
||||||
|
|
||||||
|
if updated_successfully
|
||||||
|
redirect_to project_tree_path(@project, @id), notice: "Your changes have been successfully commited"
|
||||||
|
else
|
||||||
|
flash[:notice] = "Your changes could not be commited, because the file has been changed"
|
||||||
|
render :edit
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def edit_requirements
|
||||||
|
unless @tree.is_blob? && @tree.text?
|
||||||
|
redirect_to project_tree_path(@project, @id), notice: "You can only edit text files"
|
||||||
|
end
|
||||||
|
|
||||||
|
allowed = if project.protected_branch? @ref
|
||||||
|
can?(current_user, :push_code_to_protected_branches, project)
|
||||||
|
else
|
||||||
|
can?(current_user, :push_code, project)
|
||||||
|
end
|
||||||
|
|
||||||
|
return access_denied! unless allowed
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
93
app/decorators/commit_decorator.rb
Normal file
93
app/decorators/commit_decorator.rb
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
class CommitDecorator < ApplicationDecorator
|
||||||
|
decorates :commit
|
||||||
|
|
||||||
|
# Returns a string describing the commit for use in a link title
|
||||||
|
#
|
||||||
|
# Example
|
||||||
|
#
|
||||||
|
# "Commit: Alex Denisov - Project git clone panel"
|
||||||
|
def link_title
|
||||||
|
"Commit: #{author_name} - #{title}"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns the commits title.
|
||||||
|
#
|
||||||
|
# Usually, the commit title is the first line of the commit message.
|
||||||
|
# In case this first line is longer than 80 characters, it is cut off
|
||||||
|
# after 70 characters and ellipses (`&hellp;`) are appended.
|
||||||
|
def title
|
||||||
|
title = safe_message
|
||||||
|
|
||||||
|
return no_commit_message if title.blank?
|
||||||
|
|
||||||
|
title_end = title.index(/\n/)
|
||||||
|
if (!title_end && title.length > 80) || (title_end && title_end > 80)
|
||||||
|
title[0..69] << "…".html_safe
|
||||||
|
else
|
||||||
|
title.split(/\n/, 2).first
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns the commits description
|
||||||
|
#
|
||||||
|
# cut off, ellipses (`&hellp;`) are prepended to the commit message.
|
||||||
|
def description
|
||||||
|
description = safe_message
|
||||||
|
|
||||||
|
title_end = description.index(/\n/)
|
||||||
|
if (!title_end && description.length > 80) || (title_end && title_end > 80)
|
||||||
|
"…".html_safe << description[70..-1]
|
||||||
|
else
|
||||||
|
description.split(/\n/, 2)[1].try(:chomp)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns a link to the commit author. If the author has a matching user and
|
||||||
|
# is a member of the current @project it will link to the team member page.
|
||||||
|
# Otherwise it will link to the author email as specified in the commit.
|
||||||
|
#
|
||||||
|
# options:
|
||||||
|
# avatar: true will prepend the avatar image
|
||||||
|
# size: size of the avatar image in px
|
||||||
|
def author_link(options = {})
|
||||||
|
person_link(options.merge source: :author)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Just like #author_link but for the committer.
|
||||||
|
def committer_link(options = {})
|
||||||
|
person_link(options.merge source: :committer)
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def no_commit_message
|
||||||
|
"--no commit message"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Private: Returns a link to a person. If the person has a matching user and
|
||||||
|
# is a member of the current @project it will link to the team member page.
|
||||||
|
# Otherwise it will link to the person email as specified in the commit.
|
||||||
|
#
|
||||||
|
# options:
|
||||||
|
# source: one of :author or :committer
|
||||||
|
# avatar: true will prepend the avatar image
|
||||||
|
# size: size of the avatar image in px
|
||||||
|
def person_link(options = {})
|
||||||
|
source_name = send "#{options[:source]}_name".to_sym
|
||||||
|
source_email = send "#{options[:source]}_email".to_sym
|
||||||
|
text = if options[:avatar]
|
||||||
|
avatar = h.image_tag h.gravatar_icon(source_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: ""
|
||||||
|
%Q{#{avatar} <span class="commit-#{options[:source]}-name">#{source_name}</span>}
|
||||||
|
else
|
||||||
|
source_name
|
||||||
|
end
|
||||||
|
|
||||||
|
user = User.where('name like ? or email like ?', source_name, source_email).first
|
||||||
|
|
||||||
|
if user.nil?
|
||||||
|
h.mail_to(source_email, text.html_safe, class: "commit-#{options[:source]}-link")
|
||||||
|
else
|
||||||
|
h.link_to(text.html_safe, h.user_path(user), class: "commit-#{options[:source]}-link")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
33
app/decorators/tree_decorator.rb
Normal file
33
app/decorators/tree_decorator.rb
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
class TreeDecorator < ApplicationDecorator
|
||||||
|
decorates :tree
|
||||||
|
|
||||||
|
def breadcrumbs(max_links = 2)
|
||||||
|
if path
|
||||||
|
part_path = ""
|
||||||
|
parts = path.split("\/")
|
||||||
|
|
||||||
|
yield('..', nil) if parts.count > max_links
|
||||||
|
|
||||||
|
parts.each do |part|
|
||||||
|
part_path = File.join(part_path, part) unless part_path.empty?
|
||||||
|
part_path = part if part_path.empty?
|
||||||
|
|
||||||
|
next unless parts.last(2).include?(part) if parts.count > max_links
|
||||||
|
yield(part, h.tree_join(ref, part_path))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def up_dir?
|
||||||
|
path.present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def up_dir_path
|
||||||
|
file = File.join(path, "..")
|
||||||
|
h.tree_join(ref, file)
|
||||||
|
end
|
||||||
|
|
||||||
|
def readme
|
||||||
|
@readme ||= contents.find { |c| c.is_a?(Grit::Blob) and c.name =~ /^readme/i }
|
||||||
|
end
|
||||||
|
end
|
|
@ -96,7 +96,7 @@ module ApplicationHelper
|
||||||
]
|
]
|
||||||
|
|
||||||
project_nav = []
|
project_nav = []
|
||||||
if @project && @project.repository.exists? && @project.repository.root_ref
|
if @project && @project.repository && @project.repository.root_ref
|
||||||
project_nav = [
|
project_nav = [
|
||||||
{ label: "#{simple_sanitize(@project.name_with_namespace)} - Issues", url: project_issues_path(@project) },
|
{ label: "#{simple_sanitize(@project.name_with_namespace)} - Issues", url: project_issues_path(@project) },
|
||||||
{ label: "#{simple_sanitize(@project.name_with_namespace)} - Commits", url: project_commits_path(@project, @ref || @project.repository.root_ref) },
|
{ label: "#{simple_sanitize(@project.name_with_namespace)} - Commits", url: project_commits_path(@project, @ref || @project.repository.root_ref) },
|
||||||
|
|
|
@ -1,20 +1,4 @@
|
||||||
module CommitsHelper
|
module CommitsHelper
|
||||||
# Returns a link to the commit author. If the author has a matching user and
|
|
||||||
# is a member of the current @project it will link to the team member page.
|
|
||||||
# Otherwise it will link to the author email as specified in the commit.
|
|
||||||
#
|
|
||||||
# options:
|
|
||||||
# avatar: true will prepend the avatar image
|
|
||||||
# size: size of the avatar image in px
|
|
||||||
def commit_author_link(commit, options = {})
|
|
||||||
commit_person_link(commit, options.merge(source: :author))
|
|
||||||
end
|
|
||||||
|
|
||||||
# Just like #author_link but for the committer.
|
|
||||||
def commit_committer_link(commit, options = {})
|
|
||||||
commit_person_link(commit, options.merge(source: :committer))
|
|
||||||
end
|
|
||||||
|
|
||||||
def identification_type(line)
|
def identification_type(line)
|
||||||
if line[0] == "+"
|
if line[0] == "+"
|
||||||
"new"
|
"new"
|
||||||
|
@ -109,8 +93,10 @@ module CommitsHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def commit_to_html commit
|
def commit_to_html commit
|
||||||
|
if commit.model
|
||||||
escape_javascript(render 'commits/commit', commit: commit)
|
escape_javascript(render 'commits/commit', commit: commit)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def diff_line_content(line)
|
def diff_line_content(line)
|
||||||
if line.blank?
|
if line.blank?
|
||||||
|
@ -119,58 +105,4 @@ module CommitsHelper
|
||||||
line
|
line
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Breadcrumb links for a Project and, if applicable, a tree path
|
|
||||||
def commits_breadcrumbs
|
|
||||||
return unless @project && @ref
|
|
||||||
|
|
||||||
# Add the root project link and the arrow icon
|
|
||||||
crumbs = content_tag(:li) do
|
|
||||||
content_tag(:span, nil, class: 'arrow') +
|
|
||||||
link_to(@project.name, project_commits_path(@project, @ref))
|
|
||||||
end
|
|
||||||
|
|
||||||
if @path
|
|
||||||
parts = @path.split('/')
|
|
||||||
|
|
||||||
parts.each_with_index do |part, i|
|
|
||||||
crumbs += content_tag(:span, '/', class: 'divider')
|
|
||||||
crumbs += content_tag(:li) do
|
|
||||||
# The text is just the individual part, but the link needs all the parts before it
|
|
||||||
link_to part, project_commits_path(@project, tree_join(@ref, parts[0..i].join('/')))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
crumbs.html_safe
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
# Private: Returns a link to a person. If the person has a matching user and
|
|
||||||
# is a member of the current @project it will link to the team member page.
|
|
||||||
# Otherwise it will link to the person email as specified in the commit.
|
|
||||||
#
|
|
||||||
# options:
|
|
||||||
# source: one of :author or :committer
|
|
||||||
# avatar: true will prepend the avatar image
|
|
||||||
# size: size of the avatar image in px
|
|
||||||
def commit_person_link(commit, options = {})
|
|
||||||
source_name = commit.send "#{options[:source]}_name".to_sym
|
|
||||||
source_email = commit.send "#{options[:source]}_email".to_sym
|
|
||||||
text = if options[:avatar]
|
|
||||||
avatar = image_tag(gravatar_icon(source_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]}", width: options[:size], alt: "")
|
|
||||||
%Q{#{avatar} <span class="commit-#{options[:source]}-name">#{source_name}</span>}
|
|
||||||
else
|
|
||||||
source_name
|
|
||||||
end
|
|
||||||
|
|
||||||
user = User.where('name like ? or email like ?', source_name, source_email).first
|
|
||||||
|
|
||||||
if user.nil?
|
|
||||||
mail_to(source_email, text.html_safe, class: "commit-#{options[:source]}-link")
|
|
||||||
else
|
|
||||||
link_to(text.html_safe, user_path(user), class: "commit-#{options[:source]}-link")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,13 +1,6 @@
|
||||||
module GraphHelper
|
module GraphHelper
|
||||||
def get_refs(commit)
|
def join_with_space(ary)
|
||||||
refs = ""
|
ary.collect{|r|r.name}.join(" ") unless ary.nil?
|
||||||
refs += commit.refs.collect{|r|r.name}.join(" ") if commit.refs
|
|
||||||
|
|
||||||
# append note count
|
|
||||||
notes = @project.notes.for_commit_id(commit.id)
|
|
||||||
refs += "[#{notes.count}]" if notes.any?
|
|
||||||
|
|
||||||
refs
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def parents_zip_spaces(parents, parent_spaces)
|
def parents_zip_spaces(parents, parent_spaces)
|
||||||
|
|
|
@ -48,19 +48,7 @@ module IssuesHelper
|
||||||
if @project.used_default_issues_tracker?
|
if @project.used_default_issues_tracker?
|
||||||
project_issues_filter_path(@project)
|
project_issues_filter_path(@project)
|
||||||
else
|
else
|
||||||
url = Gitlab.config.issues_tracker[@project.issues_tracker]["project_url"]
|
url = Settings[:issues_tracker][@project.issues_tracker]["project_url"]
|
||||||
url.gsub(':project_id', @project.id.to_s)
|
|
||||||
.gsub(':issues_tracker_id', @project.issues_tracker_id.to_s)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def url_for_new_issue
|
|
||||||
return "" if @project.nil?
|
|
||||||
|
|
||||||
if @project.used_default_issues_tracker?
|
|
||||||
url = new_project_issue_path project_id: @project
|
|
||||||
else
|
|
||||||
url = Gitlab.config.issues_tracker[@project.issues_tracker]["new_issue_url"]
|
|
||||||
url.gsub(':project_id', @project.id.to_s)
|
url.gsub(':project_id', @project.id.to_s)
|
||||||
.gsub(':issues_tracker_id', @project.issues_tracker_id.to_s)
|
.gsub(':issues_tracker_id', @project.issues_tracker_id.to_s)
|
||||||
end
|
end
|
||||||
|
@ -72,7 +60,7 @@ module IssuesHelper
|
||||||
if @project.used_default_issues_tracker?
|
if @project.used_default_issues_tracker?
|
||||||
url = project_issue_url project_id: @project, id: issue_id
|
url = project_issue_url project_id: @project, id: issue_id
|
||||||
else
|
else
|
||||||
url = Gitlab.config.issues_tracker[@project.issues_tracker]["issues_url"]
|
url = Settings[:issues_tracker][@project.issues_tracker]["issues_url"]
|
||||||
url.gsub(':id', issue_id.to_s)
|
url.gsub(':id', issue_id.to_s)
|
||||||
.gsub(':project_id', @project.id.to_s)
|
.gsub(':project_id', @project.id.to_s)
|
||||||
.gsub(':issues_tracker_id', @project.issues_tracker_id.to_s)
|
.gsub(':issues_tracker_id', @project.issues_tracker_id.to_s)
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
module NotificationsHelper
|
|
||||||
end
|
|
|
@ -70,26 +70,28 @@ module TreeHelper
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def tree_breadcrumbs(tree, max_links = 2)
|
# Breadcrumb links for a Project and, if applicable, a tree path
|
||||||
if tree.path
|
def breadcrumbs
|
||||||
part_path = ""
|
return unless @project && @ref
|
||||||
parts = tree.path.split("\/")
|
|
||||||
|
|
||||||
yield('..', nil) if parts.count > max_links
|
# Add the root project link and the arrow icon
|
||||||
|
crumbs = content_tag(:li) do
|
||||||
|
content_tag(:span, nil, class: 'arrow') +
|
||||||
|
link_to(@project.name, project_commits_path(@project, @ref))
|
||||||
|
end
|
||||||
|
|
||||||
parts.each do |part|
|
if @path
|
||||||
part_path = File.join(part_path, part) unless part_path.empty?
|
parts = @path.split('/')
|
||||||
part_path = part if part_path.empty?
|
|
||||||
|
|
||||||
next unless parts.last(2).include?(part) if parts.count > max_links
|
parts.each_with_index do |part, i|
|
||||||
yield(part, tree_join(tree.ref, part_path))
|
crumbs += content_tag(:span, '/', class: 'divider')
|
||||||
|
crumbs += content_tag(:li) do
|
||||||
|
# The text is just the individual part, but the link needs all the parts before it
|
||||||
|
link_to part, project_commits_path(@project, tree_join(@ref, parts[0..i].join('/')))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def up_dir_path tree
|
crumbs.html_safe
|
||||||
file = File.join(tree.path, "..")
|
|
||||||
tree_join(tree.ref, file)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
module Emails
|
module Emails
|
||||||
module Issues
|
module Issues
|
||||||
def new_issue_email(recipient_id, issue_id)
|
def new_issue_email(issue_id)
|
||||||
@issue = Issue.find(issue_id)
|
@issue = Issue.find(issue_id)
|
||||||
@project = @issue.project
|
@project = @issue.project
|
||||||
mail(to: recipient(recipient_id), subject: subject("new issue ##{@issue.id}", @issue.title))
|
mail(to: @issue.assignee_email, subject: subject("new issue ##{@issue.id}", @issue.title))
|
||||||
end
|
end
|
||||||
|
|
||||||
def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id)
|
def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id)
|
||||||
|
@ -13,14 +13,6 @@ module Emails
|
||||||
mail(to: recipient(recipient_id), subject: subject("changed issue ##{@issue.id}", @issue.title))
|
mail(to: recipient(recipient_id), subject: subject("changed issue ##{@issue.id}", @issue.title))
|
||||||
end
|
end
|
||||||
|
|
||||||
def closed_issue_email(recipient_id, issue_id, updated_by_user_id)
|
|
||||||
@issue = Issue.find issue_id
|
|
||||||
@project = @issue.project
|
|
||||||
@updated_by = User.find updated_by_user_id
|
|
||||||
mail(to: recipient(recipient_id),
|
|
||||||
subject: subject("Closed issue ##{@issue.id}", @issue.title))
|
|
||||||
end
|
|
||||||
|
|
||||||
def issue_status_changed_email(recipient_id, issue_id, status, updated_by_user_id)
|
def issue_status_changed_email(recipient_id, issue_id, status, updated_by_user_id)
|
||||||
@issue = Issue.find issue_id
|
@issue = Issue.find issue_id
|
||||||
@issue_status = status
|
@issue_status = status
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
module Emails
|
module Emails
|
||||||
module MergeRequests
|
module MergeRequests
|
||||||
def new_merge_request_email(recipient_id, merge_request_id)
|
def new_merge_request_email(merge_request_id)
|
||||||
@merge_request = MergeRequest.find(merge_request_id)
|
@merge_request = MergeRequest.find(merge_request_id)
|
||||||
@project = @merge_request.project
|
@project = @merge_request.project
|
||||||
mail(to: recipient(recipient_id), subject: subject("new merge request !#{@merge_request.id}", @merge_request.title))
|
mail(to: @merge_request.assignee_email, subject: subject("new merge request !#{@merge_request.id}", @merge_request.title))
|
||||||
end
|
end
|
||||||
|
|
||||||
def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id)
|
def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id)
|
||||||
|
@ -12,18 +12,5 @@ module Emails
|
||||||
@project = @merge_request.project
|
@project = @merge_request.project
|
||||||
mail(to: recipient(recipient_id), subject: subject("changed merge request !#{@merge_request.id}", @merge_request.title))
|
mail(to: recipient(recipient_id), subject: subject("changed merge request !#{@merge_request.id}", @merge_request.title))
|
||||||
end
|
end
|
||||||
|
|
||||||
def closed_merge_request_email(recipient_id, merge_request_id, updated_by_user_id)
|
|
||||||
@merge_request = MergeRequest.find(merge_request_id)
|
|
||||||
@project = @merge_request.project
|
|
||||||
@updated_by = User.find updated_by_user_id
|
|
||||||
mail(to: recipient(recipient_id), subject: subject("Closed merge request !#{@merge_request.id}", @merge_request.title))
|
|
||||||
end
|
|
||||||
|
|
||||||
def merged_merge_request_email(recipient_id, merge_request_id)
|
|
||||||
@merge_request = MergeRequest.find(merge_request_id)
|
|
||||||
@project = @merge_request.project
|
|
||||||
mail(to: recipient(recipient_id), subject: subject("Accepted merge request !#{@merge_request.id}", @merge_request.title))
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,6 +3,7 @@ module Emails
|
||||||
def note_commit_email(recipient_id, note_id)
|
def note_commit_email(recipient_id, note_id)
|
||||||
@note = Note.find(note_id)
|
@note = Note.find(note_id)
|
||||||
@commit = @note.noteable
|
@commit = @note.noteable
|
||||||
|
@commit = CommitDecorator.decorate(@commit)
|
||||||
@project = @note.project
|
@project = @note.project
|
||||||
mail(to: recipient(recipient_id), subject: subject("note for commit #{@commit.short_id}", @commit.title))
|
mail(to: recipient(recipient_id), subject: subject("note for commit #{@commit.short_id}", @commit.title))
|
||||||
end
|
end
|
||||||
|
|
|
@ -41,7 +41,7 @@ class Ability
|
||||||
rules << project_guest_rules
|
rules << project_guest_rules
|
||||||
end
|
end
|
||||||
|
|
||||||
if project.owner == user || user.admin?
|
if project.owner == user
|
||||||
rules << project_admin_rules
|
rules << project_admin_rules
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -8,70 +8,168 @@ class Commit
|
||||||
#
|
#
|
||||||
DIFF_SAFE_SIZE = 100
|
DIFF_SAFE_SIZE = 100
|
||||||
|
|
||||||
def self.decorate(commits)
|
attr_accessor :commit, :head, :refs
|
||||||
commits.map { |c| self.new(c) }
|
|
||||||
|
delegate :message, :authored_date, :committed_date, :parents, :sha,
|
||||||
|
:date, :committer, :author, :diffs, :tree, :id, :stats,
|
||||||
|
:to_patch, to: :commit
|
||||||
|
|
||||||
|
class << self
|
||||||
|
def find_or_first(repo, commit_id = nil, root_ref)
|
||||||
|
commit = if commit_id
|
||||||
|
repo.commit(commit_id)
|
||||||
|
else
|
||||||
|
repo.commits(root_ref).first
|
||||||
end
|
end
|
||||||
|
|
||||||
attr_accessor :raw
|
Commit.new(commit) if commit
|
||||||
|
end
|
||||||
|
|
||||||
def initialize(raw_commit)
|
def fresh_commits(repo, n = 10)
|
||||||
|
commits = repo.heads.map do |h|
|
||||||
|
repo.commits(h.name, n).map { |c| Commit.new(c, h) }
|
||||||
|
end.flatten.uniq { |c| c.id }
|
||||||
|
|
||||||
|
commits.sort! do |x, y|
|
||||||
|
y.committed_date <=> x.committed_date
|
||||||
|
end
|
||||||
|
|
||||||
|
commits[0...n]
|
||||||
|
end
|
||||||
|
|
||||||
|
def commits_with_refs(repo, n = 20)
|
||||||
|
commits = repo.branches.map { |ref| Commit.new(ref.commit, ref) }
|
||||||
|
|
||||||
|
commits.sort! do |x, y|
|
||||||
|
y.committed_date <=> x.committed_date
|
||||||
|
end
|
||||||
|
|
||||||
|
commits[0..n]
|
||||||
|
end
|
||||||
|
|
||||||
|
def commits_since(repo, date)
|
||||||
|
commits = repo.heads.map do |h|
|
||||||
|
repo.log(h.name, nil, since: date).each { |c| Commit.new(c, h) }
|
||||||
|
end.flatten.uniq { |c| c.id }
|
||||||
|
|
||||||
|
commits.sort! do |x, y|
|
||||||
|
y.committed_date <=> x.committed_date
|
||||||
|
end
|
||||||
|
|
||||||
|
commits
|
||||||
|
end
|
||||||
|
|
||||||
|
def commits(repo, ref, path = nil, limit = nil, offset = nil)
|
||||||
|
if path
|
||||||
|
repo.log(ref, path, max_count: limit, skip: offset)
|
||||||
|
elsif limit && offset
|
||||||
|
repo.commits(ref, limit, offset)
|
||||||
|
else
|
||||||
|
repo.commits(ref)
|
||||||
|
end.map{ |c| Commit.new(c) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def commits_between(repo, from, to)
|
||||||
|
repo.commits_between(from, to).map { |c| Commit.new(c) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def compare(project, from, to)
|
||||||
|
result = {
|
||||||
|
commits: [],
|
||||||
|
diffs: [],
|
||||||
|
commit: nil,
|
||||||
|
same: false
|
||||||
|
}
|
||||||
|
|
||||||
|
return result unless from && to
|
||||||
|
|
||||||
|
first = project.repository.commit(to.try(:strip))
|
||||||
|
last = project.repository.commit(from.try(:strip))
|
||||||
|
|
||||||
|
if first && last
|
||||||
|
result[:same] = (first.id == last.id)
|
||||||
|
result[:commits] = project.repo.commits_between(last.id, first.id).map {|c| Commit.new(c)}
|
||||||
|
|
||||||
|
# Dont load diff for 100+ commits
|
||||||
|
result[:diffs] = if result[:commits].size > 100
|
||||||
|
[]
|
||||||
|
else
|
||||||
|
project.repo.diff(last.id, first.id) rescue []
|
||||||
|
end
|
||||||
|
|
||||||
|
result[:commit] = Commit.new(first)
|
||||||
|
end
|
||||||
|
|
||||||
|
result
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize(raw_commit, head = nil)
|
||||||
raise "Nil as raw commit passed" unless raw_commit
|
raise "Nil as raw commit passed" unless raw_commit
|
||||||
|
|
||||||
@raw = raw_commit
|
@commit = raw_commit
|
||||||
|
@head = head
|
||||||
end
|
end
|
||||||
|
|
||||||
def id
|
def short_id(length = 10)
|
||||||
@raw.id
|
id.to_s[0..length]
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a string describing the commit for use in a link title
|
def safe_message
|
||||||
#
|
@safe_message ||= message
|
||||||
# Example
|
|
||||||
#
|
|
||||||
# "Commit: Alex Denisov - Project git clone panel"
|
|
||||||
def link_title
|
|
||||||
"Commit: #{author_name} - #{title}"
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns the commits title.
|
def created_at
|
||||||
#
|
committed_date
|
||||||
# Usually, the commit title is the first line of the commit message.
|
end
|
||||||
# In case this first line is longer than 80 characters, it is cut off
|
|
||||||
# after 70 characters and ellipses (`&hellp;`) are appended.
|
|
||||||
def title
|
|
||||||
title = safe_message
|
|
||||||
|
|
||||||
return no_commit_message if title.blank?
|
def author_email
|
||||||
|
author.email
|
||||||
|
end
|
||||||
|
|
||||||
title_end = title.index(/\n/)
|
def author_name
|
||||||
if (!title_end && title.length > 80) || (title_end && title_end > 80)
|
author.name
|
||||||
title[0..69] << "…".html_safe
|
end
|
||||||
|
|
||||||
|
# Was this commit committed by a different person than the original author?
|
||||||
|
def different_committer?
|
||||||
|
author_name != committer_name || author_email != committer_email
|
||||||
|
end
|
||||||
|
|
||||||
|
def committer_name
|
||||||
|
committer.name
|
||||||
|
end
|
||||||
|
|
||||||
|
def committer_email
|
||||||
|
committer.email
|
||||||
|
end
|
||||||
|
|
||||||
|
def prev_commit
|
||||||
|
@prev_commit ||= if parents.present?
|
||||||
|
Commit.new(parents.first)
|
||||||
else
|
else
|
||||||
title.split(/\n/, 2).first
|
nil
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns the commits description
|
def prev_commit_id
|
||||||
|
prev_commit.try :id
|
||||||
|
end
|
||||||
|
|
||||||
|
# Shows the diff between the commit's parent and the commit.
|
||||||
#
|
#
|
||||||
# cut off, ellipses (`&hellp;`) are prepended to the commit message.
|
# Cuts out the header and stats from #to_patch and returns only the diff.
|
||||||
def description
|
def to_diff
|
||||||
description = safe_message
|
# see Grit::Commit#show
|
||||||
|
patch = to_patch
|
||||||
|
|
||||||
title_end = description.index(/\n/)
|
# discard lines before the diff
|
||||||
if (!title_end && description.length > 80) || (title_end && title_end > 80)
|
lines = patch.split("\n")
|
||||||
"…".html_safe << description[70..-1]
|
while !lines.first.start_with?("diff --git") do
|
||||||
else
|
lines.shift
|
||||||
description.split(/\n/, 2)[1].try(:chomp)
|
end
|
||||||
end
|
lines.pop if lines.last =~ /^[\d.]+$/ # Git version
|
||||||
end
|
lines.pop if lines.last == "-- " # end of diff
|
||||||
|
lines.join("\n")
|
||||||
def method_missing(m, *args, &block)
|
|
||||||
@raw.send(m, *args, &block)
|
|
||||||
end
|
|
||||||
|
|
||||||
def respond_to?(method)
|
|
||||||
return true if @raw.respond_to?(method)
|
|
||||||
|
|
||||||
super
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -50,7 +50,7 @@ class GollumWiki
|
||||||
# Returns the last 30 Commit objects across the entire
|
# Returns the last 30 Commit objects across the entire
|
||||||
# repository.
|
# repository.
|
||||||
def recent_history
|
def recent_history
|
||||||
Gitlab::Git::Commit.fresh_commits(wiki.repo, 30)
|
Commit.fresh_commits(wiki.repo, 30)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Finds a page within the repository based on a tile
|
# Finds a page within the repository based on a tile
|
||||||
|
@ -90,17 +90,13 @@ class GollumWiki
|
||||||
private
|
private
|
||||||
|
|
||||||
def create_repo!
|
def create_repo!
|
||||||
if init_repo(path_with_namespace)
|
if gitlab_shell.add_repository(path_with_namespace)
|
||||||
Gollum::Wiki.new(path_to_repo)
|
Gollum::Wiki.new(path_to_repo)
|
||||||
else
|
else
|
||||||
raise CouldNotCreateWikiError
|
raise CouldNotCreateWikiError
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def init_repo(path_with_namespace)
|
|
||||||
gitlab_shell.add_repository(path_with_namespace)
|
|
||||||
end
|
|
||||||
|
|
||||||
def commit_details(action, message = nil, title = nil)
|
def commit_details(action, message = nil, title = nil)
|
||||||
commit_message = message || default_message(action, title)
|
commit_message = message || default_message(action, title)
|
||||||
|
|
||||||
|
@ -118,4 +114,5 @@ class GollumWiki
|
||||||
def path_to_repo
|
def path_to_repo
|
||||||
@path_to_repo ||= File.join(Gitlab.config.gitlab_shell.repos_path, "#{path_with_namespace}.git")
|
@path_to_repo ||= File.join(Gitlab.config.gitlab_shell.repos_path, "#{path_with_namespace}.git")
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -152,17 +152,7 @@ class MergeRequest < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def commits
|
def commits
|
||||||
if st_commits.present?
|
st_commits || []
|
||||||
# check if merge request commits are valid
|
|
||||||
if st_commits.first.respond_to?(:short_id)
|
|
||||||
st_commits
|
|
||||||
else
|
|
||||||
# if commits are invalid - simply reload it from repo
|
|
||||||
reloaded_commits
|
|
||||||
end
|
|
||||||
else
|
|
||||||
[]
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def probably_merged?
|
def probably_merged?
|
||||||
|
@ -172,12 +162,6 @@ class MergeRequest < ActiveRecord::Base
|
||||||
|
|
||||||
def reloaded_commits
|
def reloaded_commits
|
||||||
if opened? && unmerged_commits.any?
|
if opened? && unmerged_commits.any?
|
||||||
# we need to reset st_commits field first
|
|
||||||
# in order to prevent internal rails comparison
|
|
||||||
self.st_commits = []
|
|
||||||
save
|
|
||||||
|
|
||||||
# Then we can safely write unmerged commits
|
|
||||||
self.st_commits = unmerged_commits
|
self.st_commits = unmerged_commits
|
||||||
save
|
save
|
||||||
end
|
end
|
||||||
|
@ -185,8 +169,9 @@ class MergeRequest < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def unmerged_commits
|
def unmerged_commits
|
||||||
self.project.repository.
|
self.project.repo.
|
||||||
commits_between(self.target_branch, self.source_branch).
|
commits_between(self.target_branch, self.source_branch).
|
||||||
|
map {|c| Commit.new(c)}.
|
||||||
sort_by(&:created_at).
|
sort_by(&:created_at).
|
||||||
reverse
|
reverse
|
||||||
end
|
end
|
||||||
|
|
|
@ -13,8 +13,6 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
class Namespace < ActiveRecord::Base
|
class Namespace < ActiveRecord::Base
|
||||||
include Gitlab::ShellAdapter
|
|
||||||
|
|
||||||
attr_accessible :name, :description, :path
|
attr_accessible :name, :description, :path
|
||||||
|
|
||||||
has_many :projects, dependent: :destroy
|
has_many :projects, dependent: :destroy
|
||||||
|
@ -33,7 +31,7 @@ class Namespace < ActiveRecord::Base
|
||||||
delegate :name, to: :owner, allow_nil: true, prefix: true
|
delegate :name, to: :owner, allow_nil: true, prefix: true
|
||||||
|
|
||||||
after_create :ensure_dir_exist
|
after_create :ensure_dir_exist
|
||||||
after_update :move_dir, if: :path_changed?
|
after_update :move_dir
|
||||||
after_destroy :rm_dir
|
after_destroy :rm_dir
|
||||||
|
|
||||||
scope :root, -> { where('type IS NULL') }
|
scope :root, -> { where('type IS NULL') }
|
||||||
|
@ -55,34 +53,48 @@ class Namespace < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def ensure_dir_exist
|
def ensure_dir_exist
|
||||||
gitlab_shell.add_namespace(path)
|
unless dir_exists?
|
||||||
|
FileUtils.mkdir( namespace_full_path, mode: 0770 )
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def rm_dir
|
def dir_exists?
|
||||||
gitlab_shell.rm_namespace(path)
|
File.exists?(namespace_full_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
def namespace_full_path
|
||||||
|
@namespace_full_path ||= File.join(Gitlab.config.gitlab_shell.repos_path, path)
|
||||||
end
|
end
|
||||||
|
|
||||||
def move_dir
|
def move_dir
|
||||||
if gitlab_shell.mv_namespace(path_was, path)
|
if path_changed?
|
||||||
# If repositories moved successfully we need to remove old satellites
|
old_path = File.join(Gitlab.config.gitlab_shell.repos_path, path_was)
|
||||||
# and send update instructions to users.
|
new_path = File.join(Gitlab.config.gitlab_shell.repos_path, path)
|
||||||
# However we cannot allow rollback since we moved namespace dir
|
if File.exists?(new_path)
|
||||||
# So we basically we mute exceptions in next actions
|
raise "Already exists"
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
begin
|
begin
|
||||||
gitlab_shell.rm_satellites(path_was)
|
# Remove satellite when moving repo
|
||||||
|
if path_was.present?
|
||||||
|
satellites_path = File.join(Gitlab.config.satellites.path, path_was)
|
||||||
|
FileUtils.rm_r( satellites_path, force: true )
|
||||||
|
end
|
||||||
|
|
||||||
|
FileUtils.mv( old_path, new_path )
|
||||||
send_update_instructions
|
send_update_instructions
|
||||||
rescue
|
rescue Exception => e
|
||||||
# Returning false does not rolback after_* transaction but gives
|
raise "Namespace move error #{old_path} #{new_path}"
|
||||||
# us information about failing some of tasks
|
|
||||||
false
|
|
||||||
end
|
end
|
||||||
else
|
|
||||||
# if we cannot move namespace directory we should rollback
|
|
||||||
# db changes in order to prevent out of sync between db and fs
|
|
||||||
raise Exception.new('namespace directory cannot be moved')
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def rm_dir
|
||||||
|
dir_path = File.join(Gitlab.config.gitlab_shell.repos_path, path)
|
||||||
|
FileUtils.rm_r( dir_path, force: true )
|
||||||
|
end
|
||||||
|
|
||||||
def send_update_instructions
|
def send_update_instructions
|
||||||
projects.each(&:send_move_instructions)
|
projects.each(&:send_move_instructions)
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,7 +8,7 @@ module Network
|
||||||
attr_accessor :time, :spaces, :parent_spaces
|
attr_accessor :time, :spaces, :parent_spaces
|
||||||
|
|
||||||
def initialize(raw_commit, refs)
|
def initialize(raw_commit, refs)
|
||||||
@commit = Gitlab::Git::Commit.new(raw_commit)
|
@commit = ::Commit.new(raw_commit)
|
||||||
@time = -1
|
@time = -1
|
||||||
@spaces = []
|
@spaces = []
|
||||||
@parent_spaces = []
|
@parent_spaces = []
|
||||||
|
|
|
@ -40,12 +40,15 @@ module Network
|
||||||
def index_commits
|
def index_commits
|
||||||
days = []
|
days = []
|
||||||
@map = {}
|
@map = {}
|
||||||
@reserved = {}
|
|
||||||
|
|
||||||
@commits.each_with_index do |c,i|
|
@commits.reverse.each_with_index do |c,i|
|
||||||
c.time = i
|
c.time = i
|
||||||
days[i] = c.committed_date
|
days[i] = c.committed_date
|
||||||
@map[c.id] = c
|
@map[c.id] = c
|
||||||
|
end
|
||||||
|
|
||||||
|
@reserved = {}
|
||||||
|
days.each_index do |i|
|
||||||
@reserved[i] = []
|
@reserved[i] = []
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -132,7 +135,11 @@ module Network
|
||||||
spaces = []
|
spaces = []
|
||||||
|
|
||||||
commit.parents(@map).each do |parent|
|
commit.parents(@map).each do |parent|
|
||||||
range = commit.time..parent.time
|
range = if commit.time < parent.time then
|
||||||
|
commit.time..parent.time
|
||||||
|
else
|
||||||
|
parent.time..commit.time
|
||||||
|
end
|
||||||
|
|
||||||
space = if commit.space >= parent.space then
|
space = if commit.space >= parent.space then
|
||||||
find_free_parent_space(range, parent.space, -1, commit.space)
|
find_free_parent_space(range, parent.space, -1, commit.space)
|
||||||
|
@ -159,7 +166,7 @@ module Network
|
||||||
range.each do |i|
|
range.each do |i|
|
||||||
if i != range.first &&
|
if i != range.first &&
|
||||||
i != range.last &&
|
i != range.last &&
|
||||||
@commits[i].spaces.include?(overlap_space) then
|
@commits[reversed_index(i)].spaces.include?(overlap_space) then
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
end
|
end
|
||||||
|
@ -177,7 +184,7 @@ module Network
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
time_range = leaves.first.time..leaves.last.time
|
time_range = leaves.last.time..leaves.first.time
|
||||||
space_base = get_space_base(leaves)
|
space_base = get_space_base(leaves)
|
||||||
space = find_free_space(time_range, 2, space_base)
|
space = find_free_space(time_range, 2, space_base)
|
||||||
leaves.each do |l|
|
leaves.each do |l|
|
||||||
|
@ -191,17 +198,17 @@ module Network
|
||||||
end
|
end
|
||||||
|
|
||||||
# and mark it as reserved
|
# and mark it as reserved
|
||||||
if parent_time.nil?
|
min_time = leaves.last.time
|
||||||
min_time = leaves.first.time
|
leaves.last.parents(@map).each do |parent|
|
||||||
else
|
if parent.time < min_time
|
||||||
min_time = parent_time + 1
|
min_time = parent.time
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
max_time = leaves.last.time
|
if parent_time.nil?
|
||||||
leaves.last.parents(@map).each do |parent|
|
max_time = leaves.first.time
|
||||||
if max_time < parent.time
|
else
|
||||||
max_time = parent.time
|
max_time = parent_time - 1
|
||||||
end
|
|
||||||
end
|
end
|
||||||
mark_reserved(min_time..max_time, space)
|
mark_reserved(min_time..max_time, space)
|
||||||
|
|
||||||
|
@ -282,5 +289,9 @@ module Network
|
||||||
end
|
end
|
||||||
refs_cache
|
refs_cache
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def reversed_index(index)
|
||||||
|
-index - 1
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -22,6 +22,9 @@ class Note < ActiveRecord::Base
|
||||||
attr_accessible :note, :noteable, :noteable_id, :noteable_type, :project_id,
|
attr_accessible :note, :noteable, :noteable_id, :noteable_type, :project_id,
|
||||||
:attachment, :line_code, :commit_id
|
:attachment, :line_code, :commit_id
|
||||||
|
|
||||||
|
attr_accessor :notify
|
||||||
|
attr_accessor :notify_author
|
||||||
|
|
||||||
belongs_to :project
|
belongs_to :project
|
||||||
belongs_to :noteable, polymorphic: true
|
belongs_to :noteable, polymorphic: true
|
||||||
belongs_to :author, class_name: "User"
|
belongs_to :author, class_name: "User"
|
||||||
|
@ -140,6 +143,14 @@ class Note < ActiveRecord::Base
|
||||||
nil
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def notify
|
||||||
|
@notify ||= false
|
||||||
|
end
|
||||||
|
|
||||||
|
def notify_author
|
||||||
|
@notify_author ||= false
|
||||||
|
end
|
||||||
|
|
||||||
# Returns true if this is an upvote note,
|
# Returns true if this is an upvote note,
|
||||||
# otherwise false is returned
|
# otherwise false is returned
|
||||||
def upvote?
|
def upvote?
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
class Notification
|
|
||||||
#
|
|
||||||
# Notification levels
|
|
||||||
#
|
|
||||||
N_DISABLED = 0
|
|
||||||
N_PARTICIPATING = 1
|
|
||||||
N_WATCH = 2
|
|
||||||
|
|
||||||
attr_accessor :user
|
|
||||||
|
|
||||||
def self.notification_levels
|
|
||||||
[N_DISABLED, N_PARTICIPATING, N_WATCH]
|
|
||||||
end
|
|
||||||
|
|
||||||
def initialize(user)
|
|
||||||
@user = user
|
|
||||||
end
|
|
||||||
|
|
||||||
def disabled?
|
|
||||||
user.notification_level == N_DISABLED
|
|
||||||
end
|
|
||||||
|
|
||||||
def participating?
|
|
||||||
user.notification_level == N_PARTICIPATING
|
|
||||||
end
|
|
||||||
|
|
||||||
def watch?
|
|
||||||
user.notification_level == N_WATCH
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -18,15 +18,16 @@
|
||||||
# public :boolean default(FALSE), not null
|
# public :boolean default(FALSE), not null
|
||||||
# issues_tracker :string(255) default("gitlab"), not null
|
# issues_tracker :string(255) default("gitlab"), not null
|
||||||
# issues_tracker_id :string(255)
|
# issues_tracker_id :string(255)
|
||||||
# snippets_enabled :boolean default(TRUE), not null
|
|
||||||
#
|
#
|
||||||
|
|
||||||
require "grit"
|
require "grit"
|
||||||
|
|
||||||
class Project < ActiveRecord::Base
|
class Project < ActiveRecord::Base
|
||||||
include Gitlab::ShellAdapter
|
include Gitolited
|
||||||
extend Enumerize
|
extend Enumerize
|
||||||
|
|
||||||
|
class TransferError < StandardError; end
|
||||||
|
|
||||||
attr_accessible :name, :path, :description, :default_branch, :issues_tracker,
|
attr_accessible :name, :path, :description, :default_branch, :issues_tracker,
|
||||||
:issues_enabled, :wall_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id,
|
:issues_enabled, :wall_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id,
|
||||||
:wiki_enabled, :public, :import_url, as: [:default, :admin]
|
:wiki_enabled, :public, :import_url, as: [:default, :admin]
|
||||||
|
@ -141,7 +142,13 @@ class Project < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def repository
|
def repository
|
||||||
|
if path
|
||||||
@repository ||= Repository.new(path_with_namespace, default_branch)
|
@repository ||= Repository.new(path_with_namespace, default_branch)
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
rescue Grit::NoSuchPathError
|
||||||
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def saved?
|
def saved?
|
||||||
|
@ -326,14 +333,14 @@ class Project < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def valid_repo?
|
def valid_repo?
|
||||||
repository.exists?
|
repo
|
||||||
rescue
|
rescue
|
||||||
errors.add(:path, "Invalid repository path")
|
errors.add(:path, "Invalid repository path")
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
||||||
def empty_repo?
|
def empty_repo?
|
||||||
!repository.exists? || repository.empty?
|
!repository || repository.empty?
|
||||||
end
|
end
|
||||||
|
|
||||||
def ensure_satellite_exists
|
def ensure_satellite_exists
|
||||||
|
@ -357,25 +364,18 @@ class Project < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
def repo_exists?
|
def repo_exists?
|
||||||
@repo_exists ||= repository.exists?
|
@repo_exists ||= (repository && repository.branches.present?)
|
||||||
rescue
|
rescue
|
||||||
@repo_exists = false
|
@repo_exists = false
|
||||||
end
|
end
|
||||||
|
|
||||||
def open_branches
|
def open_branches
|
||||||
all_branches = repository.branches
|
if protected_branches.empty?
|
||||||
|
self.repo.heads
|
||||||
if protected_branches.present?
|
else
|
||||||
all_branches.reject! do |branch|
|
pnames = protected_branches.map(&:name)
|
||||||
protected_branches_names.include?(branch.name)
|
self.repo.heads.reject { |h| pnames.include?(h.name) }
|
||||||
end
|
end.sort_by(&:name)
|
||||||
end
|
|
||||||
|
|
||||||
all_branches
|
|
||||||
end
|
|
||||||
|
|
||||||
def protected_branches_names
|
|
||||||
@protected_branches_names ||= protected_branches.map(&:name)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def root_ref?(branch)
|
def root_ref?(branch)
|
||||||
|
@ -397,6 +397,6 @@ class Project < ActiveRecord::Base
|
||||||
|
|
||||||
# Check if current branch name is marked as protected in the system
|
# Check if current branch name is marked as protected in the system
|
||||||
def protected_branch? branch_name
|
def protected_branch? branch_name
|
||||||
protected_branches_names.include?(branch_name)
|
protected_branches.map(&:name).include?(branch_name)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
class ProtectedBranch < ActiveRecord::Base
|
class ProtectedBranch < ActiveRecord::Base
|
||||||
include Gitlab::ShellAdapter
|
include Gitolited
|
||||||
|
|
||||||
attr_accessible :name
|
attr_accessible :name
|
||||||
|
|
||||||
|
|
|
@ -1,45 +1,169 @@
|
||||||
class Repository
|
class Repository
|
||||||
attr_accessor :raw_repository
|
include Gitlab::Popen
|
||||||
|
|
||||||
def initialize(path_with_namespace, default_branch)
|
# Repository directory name with namespace direcotry
|
||||||
@raw_repository = Gitlab::Git::Repository.new(path_with_namespace, default_branch)
|
# Examples:
|
||||||
rescue Gitlab::Git::Repository::NoRepository
|
# gitlab/gitolite
|
||||||
nil
|
# diaspora
|
||||||
|
#
|
||||||
|
attr_accessor :path_with_namespace
|
||||||
|
|
||||||
|
# Grit repo object
|
||||||
|
attr_accessor :repo
|
||||||
|
|
||||||
|
# Default branch in the repository
|
||||||
|
attr_accessor :root_ref
|
||||||
|
|
||||||
|
def initialize(path_with_namespace, root_ref = 'master')
|
||||||
|
@root_ref = root_ref || "master"
|
||||||
|
@path_with_namespace = path_with_namespace
|
||||||
|
|
||||||
|
# Init grit repo object
|
||||||
|
repo
|
||||||
end
|
end
|
||||||
|
|
||||||
def exists?
|
def raw
|
||||||
raw_repository
|
repo
|
||||||
end
|
end
|
||||||
|
|
||||||
def empty?
|
def path_to_repo
|
||||||
raw_repository.empty?
|
@path_to_repo ||= File.join(Gitlab.config.gitlab_shell.repos_path, "#{path_with_namespace}.git")
|
||||||
end
|
end
|
||||||
|
|
||||||
def commit(id = nil)
|
def repo
|
||||||
commit = raw_repository.commit(id)
|
@repo ||= Grit::Repo.new(path_to_repo)
|
||||||
commit = Commit.new(commit) if commit
|
end
|
||||||
commit
|
|
||||||
|
def commit(commit_id = nil)
|
||||||
|
Commit.find_or_first(repo, commit_id, root_ref)
|
||||||
|
end
|
||||||
|
|
||||||
|
def fresh_commits(n = 10)
|
||||||
|
Commit.fresh_commits(repo, n)
|
||||||
|
end
|
||||||
|
|
||||||
|
def commits_with_refs(n = 20)
|
||||||
|
Commit.commits_with_refs(repo, n)
|
||||||
|
end
|
||||||
|
|
||||||
|
def commits_since(date)
|
||||||
|
Commit.commits_since(repo, date)
|
||||||
end
|
end
|
||||||
|
|
||||||
def commits(ref, path = nil, limit = nil, offset = nil)
|
def commits(ref, path = nil, limit = nil, offset = nil)
|
||||||
commits = raw_repository.commits(ref, path, limit, offset)
|
Commit.commits(repo, ref, path, limit, offset)
|
||||||
commits = Commit.decorate(commits) if commits.present?
|
|
||||||
commits
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def commits_between(target, source)
|
def last_commit_for(ref, path = nil)
|
||||||
commits = raw_repository.commits_between(target, source)
|
commits(ref, path, 1).first
|
||||||
commits = Commit.decorate(commits) if commits.present?
|
|
||||||
commits
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def method_missing(m, *args, &block)
|
def commits_between(from, to)
|
||||||
raw_repository.send(m, *args, &block)
|
Commit.commits_between(repo, from, to)
|
||||||
end
|
end
|
||||||
|
|
||||||
def respond_to?(method)
|
# Returns an Array of branch names
|
||||||
return true if raw_repository.respond_to?(method)
|
def branch_names
|
||||||
|
repo.branches.collect(&:name).sort
|
||||||
|
end
|
||||||
|
|
||||||
super
|
# Returns an Array of Branches
|
||||||
|
def branches
|
||||||
|
repo.branches.sort_by(&:name)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns an Array of tag names
|
||||||
|
def tag_names
|
||||||
|
repo.tags.collect(&:name).sort.reverse
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns an Array of Tags
|
||||||
|
def tags
|
||||||
|
repo.tags.sort_by(&:name).reverse
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns an Array of branch and tag names
|
||||||
|
def ref_names
|
||||||
|
[branch_names + tag_names].flatten
|
||||||
|
end
|
||||||
|
|
||||||
|
def heads
|
||||||
|
@heads ||= repo.heads
|
||||||
|
end
|
||||||
|
|
||||||
|
def tree(fcommit, path = nil)
|
||||||
|
fcommit = commit if fcommit == :head
|
||||||
|
tree = fcommit.tree
|
||||||
|
path ? (tree / path) : tree
|
||||||
|
end
|
||||||
|
|
||||||
|
def has_commits?
|
||||||
|
!!commit
|
||||||
|
rescue Grit::NoSuchPathError
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def empty?
|
||||||
|
!has_commits?
|
||||||
|
end
|
||||||
|
|
||||||
|
# Discovers the default branch based on the repository's available branches
|
||||||
|
#
|
||||||
|
# - If no branches are present, returns nil
|
||||||
|
# - If one branch is present, returns its name
|
||||||
|
# - If two or more branches are present, returns the one that has a name
|
||||||
|
# matching root_ref (default_branch or 'master' if default_branch is nil)
|
||||||
|
def discover_default_branch
|
||||||
|
if branch_names.length == 0
|
||||||
|
nil
|
||||||
|
elsif branch_names.length == 1
|
||||||
|
branch_names.first
|
||||||
|
else
|
||||||
|
branch_names.select { |v| v == root_ref }.first
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Archive Project to .tar.gz
|
||||||
|
#
|
||||||
|
# Already packed repo archives stored at
|
||||||
|
# app_root/tmp/repositories/project_name/project_name-commit-id.tag.gz
|
||||||
|
#
|
||||||
|
def archive_repo(ref)
|
||||||
|
ref = ref || self.root_ref
|
||||||
|
commit = self.commit(ref)
|
||||||
|
return nil unless commit
|
||||||
|
|
||||||
|
# Build file path
|
||||||
|
file_name = self.path_with_namespace.gsub("/","_") + "-" + commit.id.to_s + ".tar.gz"
|
||||||
|
storage_path = Rails.root.join("tmp", "repositories")
|
||||||
|
file_path = File.join(storage_path, self.path_with_namespace, file_name)
|
||||||
|
|
||||||
|
# Put files into a directory before archiving
|
||||||
|
prefix = File.basename(self.path_with_namespace) + "/"
|
||||||
|
|
||||||
|
# Create file if not exists
|
||||||
|
unless File.exists?(file_path)
|
||||||
|
FileUtils.mkdir_p File.dirname(file_path)
|
||||||
|
file = self.repo.archive_to_file(ref, prefix, file_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
file_path
|
||||||
|
end
|
||||||
|
|
||||||
|
# Return repo size in megabytes
|
||||||
|
# Cached in redis
|
||||||
|
def size
|
||||||
|
Rails.cache.fetch(cache_key(:size)) do
|
||||||
|
size = popen('du -s', path_to_repo).first.strip.to_i
|
||||||
|
(size.to_f / 1024).round(2)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def expire_cache
|
||||||
|
Rails.cache.delete(cache_key(:size))
|
||||||
|
end
|
||||||
|
|
||||||
|
def cache_key(type)
|
||||||
|
"#{type}:#{path_with_namespace}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -26,12 +26,4 @@ class Tree
|
||||||
def empty?
|
def empty?
|
||||||
data.blank?
|
data.blank?
|
||||||
end
|
end
|
||||||
|
|
||||||
def up_dir?
|
|
||||||
path.present?
|
|
||||||
end
|
|
||||||
|
|
||||||
def readme
|
|
||||||
@readme ||= contents.find { |c| c.is_a?(Grit::Blob) and c.name =~ /^readme/i }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -33,7 +33,6 @@
|
||||||
# can_create_team :boolean default(TRUE), not null
|
# can_create_team :boolean default(TRUE), not null
|
||||||
# state :string(255)
|
# state :string(255)
|
||||||
# color_scheme_id :integer default(1), not null
|
# color_scheme_id :integer default(1), not null
|
||||||
# notification_level :integer default(1), not null
|
|
||||||
#
|
#
|
||||||
|
|
||||||
class User < ActiveRecord::Base
|
class User < ActiveRecord::Base
|
||||||
|
@ -47,13 +46,6 @@ class User < ActiveRecord::Base
|
||||||
|
|
||||||
attr_accessor :force_random_password
|
attr_accessor :force_random_password
|
||||||
|
|
||||||
# Virtual attribute for authenticating by either username or email
|
|
||||||
attr_accessor :login
|
|
||||||
|
|
||||||
# Add login to attr_accessible
|
|
||||||
attr_accessible :login
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Relations
|
# Relations
|
||||||
#
|
#
|
||||||
|
@ -109,9 +101,6 @@ class User < ActiveRecord::Base
|
||||||
format: { with: Gitlab::Regex.username_regex,
|
format: { with: Gitlab::Regex.username_regex,
|
||||||
message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
|
message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
|
||||||
|
|
||||||
validates :notification_level,
|
|
||||||
inclusion: { in: Notification.notification_levels },
|
|
||||||
presence: true
|
|
||||||
|
|
||||||
validate :namespace_uniq, if: ->(user) { user.username_changed? }
|
validate :namespace_uniq, if: ->(user) { user.username_changed? }
|
||||||
|
|
||||||
|
@ -151,16 +140,6 @@ class User < ActiveRecord::Base
|
||||||
# Class methods
|
# Class methods
|
||||||
#
|
#
|
||||||
class << self
|
class << self
|
||||||
# Devise method overriden to allow sing in with email or username
|
|
||||||
def find_for_database_authentication(warden_conditions)
|
|
||||||
conditions = warden_conditions.dup
|
|
||||||
if login = conditions.delete(:login)
|
|
||||||
where(conditions).where(["lower(username) = :value OR lower(email) = :value", { value: login.downcase }]).first
|
|
||||||
else
|
|
||||||
where(conditions).first
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def filter filter_name
|
def filter filter_name
|
||||||
case filter_name
|
case filter_name
|
||||||
when "admins"; self.admins
|
when "admins"; self.admins
|
||||||
|
@ -212,10 +191,6 @@ class User < ActiveRecord::Base
|
||||||
username
|
username
|
||||||
end
|
end
|
||||||
|
|
||||||
def notification
|
|
||||||
@notification ||= Notification.new(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
def generate_password
|
def generate_password
|
||||||
if self.force_random_password
|
if self.force_random_password
|
||||||
self.password = self.password_confirmation = Devise.friendly_token.first(8)
|
self.password = self.password_confirmation = Devise.friendly_token.first(8)
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
class UsersProject < ActiveRecord::Base
|
class UsersProject < ActiveRecord::Base
|
||||||
include Gitlab::ShellAdapter
|
include Gitolited
|
||||||
|
|
||||||
GUEST = 10
|
GUEST = 10
|
||||||
REPORTER = 20
|
REPORTER = 20
|
||||||
|
@ -38,7 +38,7 @@ class UsersProject < ActiveRecord::Base
|
||||||
scope :masters, -> { where(project_access: MASTER) }
|
scope :masters, -> { where(project_access: MASTER) }
|
||||||
|
|
||||||
scope :in_project, ->(project) { where(project_id: project.id) }
|
scope :in_project, ->(project) { where(project_id: project.id) }
|
||||||
scope :in_projects, ->(projects) { where(project_id: projects.map { |p| p.id }) }
|
scope :in_projects, ->(projects) { where(project_id: project_ids) }
|
||||||
scope :with_user, ->(user) { where(user_id: user.id) }
|
scope :with_user, ->(user) { where(user_id: user.id) }
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
|
|
|
@ -79,14 +79,14 @@ class WikiPage
|
||||||
def version
|
def version
|
||||||
return nil unless persisted?
|
return nil unless persisted?
|
||||||
|
|
||||||
@version ||= Commit.new(Gitlab::Git::Commit.new(@page.version))
|
@version ||= Commit.new(@page.version)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns an array of Gitlab Commit instances.
|
# Returns an array of Gitlab Commit instances.
|
||||||
def versions
|
def versions
|
||||||
return [] unless persisted?
|
return [] unless persisted?
|
||||||
|
|
||||||
@page.versions.map { |v| Commit.new(Gitlab::Git::Commit.new(v)) }
|
@page.versions.map { |v| Commit.new(v) }
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns the Date that this latest version was
|
# Returns the Date that this latest version was
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
class ActivityObserver < BaseObserver
|
class ActivityObserver < ActiveRecord::Observer
|
||||||
observe :issue, :merge_request, :note, :milestone
|
observe :issue, :merge_request, :note, :milestone
|
||||||
|
|
||||||
def after_create(record)
|
def after_create(record)
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
class BaseObserver < ActiveRecord::Observer
|
|
||||||
def notification
|
|
||||||
NotificationService.new
|
|
||||||
end
|
|
||||||
|
|
||||||
def log_info message
|
|
||||||
Gitlab::AppLogger.info message
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,30 +1,42 @@
|
||||||
class IssueObserver < BaseObserver
|
class IssueObserver < ActiveRecord::Observer
|
||||||
cattr_accessor :current_user
|
cattr_accessor :current_user
|
||||||
|
|
||||||
def after_create(issue)
|
def after_create(issue)
|
||||||
notification.new_issue(issue, current_user)
|
if issue.assignee && issue.assignee != current_user
|
||||||
|
Notify.delay.new_issue_email(issue.id)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def after_close(issue, transition)
|
def after_close(issue, transition)
|
||||||
notification.close_issue(issue, current_user)
|
send_reassigned_email(issue) if issue.is_being_reassigned?
|
||||||
|
|
||||||
create_note(issue)
|
create_note(issue)
|
||||||
end
|
end
|
||||||
|
|
||||||
def after_reopen(issue, transition)
|
def after_reopen(issue, transition)
|
||||||
|
send_reassigned_email(issue) if issue.is_being_reassigned?
|
||||||
|
|
||||||
create_note(issue)
|
create_note(issue)
|
||||||
end
|
end
|
||||||
|
|
||||||
def after_update(issue)
|
def after_update(issue)
|
||||||
if issue.is_being_reassigned?
|
send_reassigned_email(issue) if issue.is_being_reassigned?
|
||||||
notification.reassigned_issue(issue, current_user)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
# Create issue note with service comment like 'Status changed to closed'
|
|
||||||
def create_note(issue)
|
def create_note(issue)
|
||||||
Note.create_status_change_note(issue, current_user, issue.state)
|
Note.create_status_change_note(issue, current_user, issue.state)
|
||||||
|
[issue.author, issue.assignee].compact.uniq.each do |recipient|
|
||||||
|
Notify.delay.issue_status_changed_email(recipient.id, issue.id, issue.state, current_user.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def send_reassigned_email(issue)
|
||||||
|
recipient_ids = [issue.assignee_id, issue.assignee_id_was].keep_if {|id| id && id != current_user.id }
|
||||||
|
|
||||||
|
recipient_ids.each do |recipient_id|
|
||||||
|
Notify.delay.reassigned_issue_email(recipient_id, issue.id, issue.assignee_id_was)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
class KeyObserver < BaseObserver
|
class KeyObserver < ActiveRecord::Observer
|
||||||
|
include Gitolited
|
||||||
|
|
||||||
def after_save(key)
|
def after_save(key)
|
||||||
GitlabShellWorker.perform_async(
|
GitlabShellWorker.perform_async(
|
||||||
:add_key,
|
:add_key,
|
||||||
|
@ -6,7 +8,8 @@ class KeyObserver < BaseObserver
|
||||||
key.key
|
key.key
|
||||||
)
|
)
|
||||||
|
|
||||||
notification.new_key(key)
|
# Notify about ssh key being added
|
||||||
|
Notify.delay.new_ssh_key_email(key.id) if key.user
|
||||||
end
|
end
|
||||||
|
|
||||||
def after_destroy(key)
|
def after_destroy(key)
|
||||||
|
|
|
@ -1,25 +1,36 @@
|
||||||
class MergeRequestObserver < BaseObserver
|
class MergeRequestObserver < ActiveRecord::Observer
|
||||||
cattr_accessor :current_user
|
cattr_accessor :current_user
|
||||||
|
|
||||||
def after_create(merge_request)
|
def after_create(merge_request)
|
||||||
notification.new_merge_request(merge_request, current_user)
|
if merge_request.assignee && merge_request.assignee != current_user
|
||||||
|
Notify.delay.new_merge_request_email(merge_request.id)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def after_close(merge_request, transition)
|
def after_close(merge_request, transition)
|
||||||
|
send_reassigned_email(merge_request) if merge_request.is_being_reassigned?
|
||||||
|
|
||||||
Note.create_status_change_note(merge_request, current_user, merge_request.state)
|
Note.create_status_change_note(merge_request, current_user, merge_request.state)
|
||||||
|
|
||||||
notification.close_mr(merge_request, current_user)
|
|
||||||
end
|
|
||||||
|
|
||||||
def after_merge(merge_request, transition)
|
|
||||||
notification.merge_mr(merge_request)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def after_reopen(merge_request, transition)
|
def after_reopen(merge_request, transition)
|
||||||
|
send_reassigned_email(merge_request) if merge_request.is_being_reassigned?
|
||||||
|
|
||||||
Note.create_status_change_note(merge_request, current_user, merge_request.state)
|
Note.create_status_change_note(merge_request, current_user, merge_request.state)
|
||||||
end
|
end
|
||||||
|
|
||||||
def after_update(merge_request)
|
def after_update(merge_request)
|
||||||
notification.reassigned_merge_request(merge_request, current_user) if merge_request.is_being_reassigned?
|
send_reassigned_email(merge_request) if merge_request.is_being_reassigned?
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def send_reassigned_email(merge_request)
|
||||||
|
recipients_ids = merge_request.assignee_id_was, merge_request.assignee_id
|
||||||
|
recipients_ids.delete current_user.id
|
||||||
|
|
||||||
|
recipients_ids.each do |recipient_id|
|
||||||
|
Notify.delay.reassigned_merge_request_email(recipient_id, merge_request.id, merge_request.assignee_id_was)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,5 +1,38 @@
|
||||||
class NoteObserver < BaseObserver
|
class NoteObserver < ActiveRecord::Observer
|
||||||
def after_create(note)
|
def after_create(note)
|
||||||
notification.new_note(note)
|
send_notify_mails(note)
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def send_notify_mails(note)
|
||||||
|
if note.notify
|
||||||
|
notify_team(note)
|
||||||
|
elsif note.notify_author
|
||||||
|
# Notify only author of resource
|
||||||
|
if note.commit_author
|
||||||
|
Notify.delay.note_commit_email(note.commit_author.id, note.id)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
# Otherwise ignore it
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Notifies the whole team except the author of note
|
||||||
|
def notify_team(note)
|
||||||
|
# Note: wall posts are not "attached" to anything, so fall back to "Wall"
|
||||||
|
noteable_type = note.noteable_type.presence || "Wall"
|
||||||
|
notify_method = "note_#{noteable_type.underscore}_email".to_sym
|
||||||
|
|
||||||
|
if Notify.respond_to? notify_method
|
||||||
|
team_without_note_author(note).map do |u|
|
||||||
|
Notify.delay.send(notify_method, u.id, note.id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def team_without_note_author(note)
|
||||||
|
note.project.users.reject { |u| u.id == note.author.id }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
class ProjectObserver < BaseObserver
|
class ProjectObserver < ActiveRecord::Observer
|
||||||
def after_create(project)
|
def after_create(project)
|
||||||
GitlabShellWorker.perform_async(
|
GitlabShellWorker.perform_async(
|
||||||
:add_repository,
|
:add_repository,
|
||||||
|
@ -27,4 +27,10 @@ class ProjectObserver < BaseObserver
|
||||||
|
|
||||||
log_info("Project \"#{project.name}\" was removed")
|
log_info("Project \"#{project.name}\" was removed")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def log_info message
|
||||||
|
Gitlab::AppLogger.info message
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
class SystemHookObserver < BaseObserver
|
class SystemHookObserver < ActiveRecord::Observer
|
||||||
observe :user, :project, :users_project
|
observe :user, :project, :users_project
|
||||||
|
|
||||||
def after_create(model)
|
def after_create(model)
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
class UserObserver < BaseObserver
|
class UserObserver < ActiveRecord::Observer
|
||||||
def after_create(user)
|
def after_create(user)
|
||||||
log_info("User \"#{user.name}\" (#{user.email}) was created")
|
log_info("User \"#{user.name}\" (#{user.email}) was created")
|
||||||
|
|
||||||
notification.new_user(user)
|
# Dont email omniauth created users
|
||||||
|
Notify.delay.new_user_email(user.id, user.password) unless user.extern_uid?
|
||||||
end
|
end
|
||||||
|
|
||||||
def after_destroy user
|
def after_destroy user
|
||||||
|
@ -18,4 +19,10 @@ class UserObserver < BaseObserver
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def log_info message
|
||||||
|
Gitlab::AppLogger.info message
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
class UsersProjectObserver < BaseObserver
|
class UsersProjectObserver < ActiveRecord::Observer
|
||||||
def after_commit(users_project)
|
def after_commit(users_project)
|
||||||
return if users_project.destroyed?
|
return if users_project.destroyed?
|
||||||
|
Notify.delay.project_access_granted_email(users_project.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
def after_create(users_project)
|
def after_create(users_project)
|
||||||
|
@ -9,12 +10,6 @@ class UsersProjectObserver < BaseObserver
|
||||||
action: Event::JOINED,
|
action: Event::JOINED,
|
||||||
author_id: users_project.user.id
|
author_id: users_project.user.id
|
||||||
)
|
)
|
||||||
|
|
||||||
notification.new_team_member(users_project)
|
|
||||||
end
|
|
||||||
|
|
||||||
def after_update(users_project)
|
|
||||||
notification.update_team_member(users_project)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def after_destroy(users_project)
|
def after_destroy(users_project)
|
||||||
|
|
|
@ -1,197 +0,0 @@
|
||||||
# NotificationService class
|
|
||||||
#
|
|
||||||
# Used for notifing users with emails about different events
|
|
||||||
#
|
|
||||||
# Ex.
|
|
||||||
# NotificationService.new.new_issue(issue, current_user)
|
|
||||||
#
|
|
||||||
class NotificationService
|
|
||||||
# Always notify user about ssh key added
|
|
||||||
# only if ssh key is not deploy key
|
|
||||||
#
|
|
||||||
# This is security email so it will be sent
|
|
||||||
# even if user disabled notifications
|
|
||||||
def new_key(key)
|
|
||||||
if key.user
|
|
||||||
Notify.delay.new_ssh_key_email(key.id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# When create an issue we should send next emails:
|
|
||||||
#
|
|
||||||
# * issue assignee if his notification level is not Disabled
|
|
||||||
# * project team members with notification level higher then Participating
|
|
||||||
#
|
|
||||||
def new_issue(issue, current_user)
|
|
||||||
new_resource_email(issue, 'new_issue_email')
|
|
||||||
end
|
|
||||||
|
|
||||||
# When we close an issue we should send next emails:
|
|
||||||
#
|
|
||||||
# * issue author if his notification level is not Disabled
|
|
||||||
# * issue assignee if his notification level is not Disabled
|
|
||||||
# * project team members with notification level higher then Participating
|
|
||||||
#
|
|
||||||
def close_issue(issue, current_user)
|
|
||||||
close_resource_email(issue, current_user, 'closed_issue_email')
|
|
||||||
end
|
|
||||||
|
|
||||||
# When we reassign an issue we should send next emails:
|
|
||||||
#
|
|
||||||
# * issue old assignee if his notification level is not Disabled
|
|
||||||
# * issue new assignee if his notification level is not Disabled
|
|
||||||
#
|
|
||||||
def reassigned_issue(issue, current_user)
|
|
||||||
reassign_resource_email(issue, current_user, 'reassigned_issue_email')
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
# When create a merge request we should send next emails:
|
|
||||||
#
|
|
||||||
# * mr assignee if his notification level is not Disabled
|
|
||||||
#
|
|
||||||
def new_merge_request(merge_request, current_user)
|
|
||||||
new_resource_email(merge_request, 'new_merge_request_email')
|
|
||||||
end
|
|
||||||
|
|
||||||
# When we reassign a merge_request we should send next emails:
|
|
||||||
#
|
|
||||||
# * merge_request old assignee if his notification level is not Disabled
|
|
||||||
# * merge_request assignee if his notification level is not Disabled
|
|
||||||
#
|
|
||||||
def reassigned_merge_request(merge_request, current_user)
|
|
||||||
reassign_resource_email(merge_request, current_user, 'reassigned_merge_request_email')
|
|
||||||
end
|
|
||||||
|
|
||||||
# When we close a merge request we should send next emails:
|
|
||||||
#
|
|
||||||
# * merge_request author if his notification level is not Disabled
|
|
||||||
# * merge_request assignee if his notification level is not Disabled
|
|
||||||
# * project team members with notification level higher then Participating
|
|
||||||
#
|
|
||||||
def close_mr(merge_request, current_user)
|
|
||||||
close_resource_email(merge_request, current_user, 'closed_merge_request_email')
|
|
||||||
end
|
|
||||||
|
|
||||||
# When we merge a merge request we should send next emails:
|
|
||||||
#
|
|
||||||
# * merge_request author if his notification level is not Disabled
|
|
||||||
# * merge_request assignee if his notification level is not Disabled
|
|
||||||
# * project team members with notification level higher then Participating
|
|
||||||
#
|
|
||||||
def merge_mr(merge_request)
|
|
||||||
recipients = reject_muted_users([merge_request.author, merge_request.assignee])
|
|
||||||
recipients = recipients.concat(project_watchers(merge_request.project)).uniq
|
|
||||||
|
|
||||||
recipients.each do |recipient|
|
|
||||||
Notify.delay.merged_merge_request_email(recipient.id, merge_request.id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Notify new user with email after creation
|
|
||||||
def new_user(user)
|
|
||||||
# Dont email omniauth created users
|
|
||||||
Notify.delay.new_user_email(user.id, user.password) unless user.extern_uid?
|
|
||||||
end
|
|
||||||
|
|
||||||
# Notify users on new note in system
|
|
||||||
#
|
|
||||||
# TODO: split on methods and refactor
|
|
||||||
#
|
|
||||||
def new_note(note)
|
|
||||||
# ignore wall messages
|
|
||||||
return true unless note.noteable_type.present?
|
|
||||||
|
|
||||||
opts = { noteable_type: note.noteable_type, project_id: note.project_id }
|
|
||||||
|
|
||||||
if note.commit_id.present?
|
|
||||||
opts.merge!(commit_id: note.commit_id)
|
|
||||||
recipients = [note.commit_author]
|
|
||||||
else
|
|
||||||
opts.merge!(noteable_id: note.noteable_id)
|
|
||||||
target = note.noteable
|
|
||||||
recipients = []
|
|
||||||
recipients << target.assignee if target.respond_to?(:assignee)
|
|
||||||
recipients << target.author if target.respond_to?(:author)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Get users who left comment in thread
|
|
||||||
recipients = recipients.concat(User.where(id: Note.where(opts).pluck(:author_id)))
|
|
||||||
|
|
||||||
# Merge project watchers
|
|
||||||
recipients = recipients.concat(project_watchers(note.project)).compact.uniq
|
|
||||||
|
|
||||||
# Reject mutes users
|
|
||||||
recipients = reject_muted_users(recipients)
|
|
||||||
|
|
||||||
# Reject author
|
|
||||||
recipients.delete(note.author)
|
|
||||||
|
|
||||||
# build notify method like 'note_commit_email'
|
|
||||||
notify_method = "note_#{note.noteable_type.underscore}_email".to_sym
|
|
||||||
|
|
||||||
recipients.each do |recipient|
|
|
||||||
Notify.delay.send(notify_method, recipient.id, note.id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def new_team_member(users_project)
|
|
||||||
Notify.delay.project_access_granted_email(users_project.id)
|
|
||||||
end
|
|
||||||
|
|
||||||
def update_team_member(users_project)
|
|
||||||
Notify.delay.project_access_granted_email(users_project.id)
|
|
||||||
end
|
|
||||||
|
|
||||||
protected
|
|
||||||
|
|
||||||
# Get project users with WATCH notification level
|
|
||||||
def project_watchers(project)
|
|
||||||
project.users.where(notification_level: Notification::N_WATCH)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Remove users with disabled notifications from array
|
|
||||||
# Also remove duplications and nil recipients
|
|
||||||
def reject_muted_users(users)
|
|
||||||
users.compact.uniq.reject do |user|
|
|
||||||
user.notification.disabled?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def new_resource_email(target, method)
|
|
||||||
recipients = reject_muted_users([target.assignee])
|
|
||||||
recipients = recipients.concat(project_watchers(target.project)).uniq
|
|
||||||
recipients.delete(target.author)
|
|
||||||
|
|
||||||
recipients.each do |recipient|
|
|
||||||
Notify.delay.send(method, recipient.id, target.id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def close_resource_email(target, current_user, method)
|
|
||||||
recipients = reject_muted_users([target.author, target.assignee])
|
|
||||||
recipients = recipients.concat(project_watchers(target.project)).uniq
|
|
||||||
recipients.delete(current_user)
|
|
||||||
|
|
||||||
recipients.each do |recipient|
|
|
||||||
Notify.delay.send(method, recipient.id, target.id, current_user.id)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def reassign_resource_email(target, current_user, method)
|
|
||||||
recipients = User.where(id: [target.assignee_id, target.assignee_id_was])
|
|
||||||
|
|
||||||
# Add watchers to email list
|
|
||||||
recipients = recipients.concat(project_watchers(target.project))
|
|
||||||
|
|
||||||
# reject users with disabled notifications
|
|
||||||
recipients = reject_muted_users(recipients)
|
|
||||||
|
|
||||||
# Reject me from recipients if I reassign an item
|
|
||||||
recipients.delete(current_user)
|
|
||||||
|
|
||||||
recipients.each do |recipient|
|
|
||||||
Notify.delay.send(method, recipient.id, target.id, target.assignee_id_was)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -3,9 +3,7 @@
|
||||||
# Used for transfer project to another namespace
|
# Used for transfer project to another namespace
|
||||||
#
|
#
|
||||||
class ProjectTransferService
|
class ProjectTransferService
|
||||||
include Gitlab::ShellAdapter
|
include Gitolited
|
||||||
|
|
||||||
class TransferError < StandardError; end
|
|
||||||
|
|
||||||
attr_accessor :project
|
attr_accessor :project
|
||||||
|
|
||||||
|
@ -21,16 +19,14 @@ class ProjectTransferService
|
||||||
project.namespace = new_namespace
|
project.namespace = new_namespace
|
||||||
project.save!
|
project.save!
|
||||||
|
|
||||||
# Move main repository
|
|
||||||
unless gitlab_shell.mv_repository(old_path, new_path)
|
unless gitlab_shell.mv_repository(old_path, new_path)
|
||||||
raise TransferError.new('Cannot move project')
|
raise TransferError.new('Cannot move project')
|
||||||
end
|
end
|
||||||
|
|
||||||
# Move wiki repo also if present
|
|
||||||
gitlab_shell.mv_repository("#{old_path}.wiki", "#{new_path}.wiki")
|
|
||||||
|
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
rescue => ex
|
||||||
|
raise Project::TransferError.new(ex.message)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
86
app/views/admin/projects/_form.html.haml
Normal file
86
app/views/admin/projects/_form.html.haml
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
= form_for [:admin, project] do |f|
|
||||||
|
-if project.errors.any?
|
||||||
|
.alert.alert-error
|
||||||
|
%ul
|
||||||
|
- project.errors.full_messages.each do |msg|
|
||||||
|
%li= msg
|
||||||
|
|
||||||
|
.clearfix.project_name_holder
|
||||||
|
= f.label :name do
|
||||||
|
Project name is
|
||||||
|
.input
|
||||||
|
= f.text_field :name, placeholder: "Example Project", class: "xxlarge"
|
||||||
|
|
||||||
|
- if project.repo_exists?
|
||||||
|
%fieldset.adv_settings
|
||||||
|
%legend Advanced settings:
|
||||||
|
.clearfix
|
||||||
|
= f.label :path do
|
||||||
|
Path
|
||||||
|
.input
|
||||||
|
= text_field_tag :ppath, @project.repository.path_to_repo, class: "xlarge", disabled: true
|
||||||
|
|
||||||
|
.clearfix
|
||||||
|
= f.label :default_branch, "Default Branch"
|
||||||
|
.input= f.select(:default_branch, @project.repository.heads.map(&:name), {}, style: "width:210px;")
|
||||||
|
|
||||||
|
%fieldset.adv_settings
|
||||||
|
%legend Features:
|
||||||
|
|
||||||
|
.clearfix
|
||||||
|
= f.label :issues_enabled, "Issues"
|
||||||
|
.input= f.check_box :issues_enabled
|
||||||
|
|
||||||
|
- if Project.issues_tracker.values.count > 1
|
||||||
|
.clearfix
|
||||||
|
= f.label :issues_tracker, "Issues tracker", class: 'control-label'
|
||||||
|
.input= f.select(:issues_tracker, Project.issues_tracker.values, {}, { disabled: !@project.issues_enabled })
|
||||||
|
|
||||||
|
.clearfix
|
||||||
|
= f.label :issues_tracker_id, "Project name or id in issues tracker", class: 'control-label'
|
||||||
|
.input= f.text_field :issues_tracker_id, class: "xxlarge", disabled: !@project.can_have_issues_tracker_id?
|
||||||
|
|
||||||
|
.clearfix
|
||||||
|
= f.label :merge_requests_enabled, "Merge Requests"
|
||||||
|
.input= f.check_box :merge_requests_enabled
|
||||||
|
|
||||||
|
.clearfix
|
||||||
|
= f.label :wall_enabled, "Wall"
|
||||||
|
.input= f.check_box :wall_enabled
|
||||||
|
|
||||||
|
.clearfix
|
||||||
|
= f.label :wiki_enabled, "Wiki"
|
||||||
|
.input= f.check_box :wiki_enabled
|
||||||
|
|
||||||
|
%fieldset.features
|
||||||
|
%legend Public mode:
|
||||||
|
.clearfix
|
||||||
|
= f.label :public do
|
||||||
|
%span Allow public http clone
|
||||||
|
.input= f.check_box :public
|
||||||
|
|
||||||
|
%fieldset.features
|
||||||
|
%legend Transfer:
|
||||||
|
.control-group
|
||||||
|
= f.label :namespace_id do
|
||||||
|
%span Namespace
|
||||||
|
.controls
|
||||||
|
= f.select :namespace_id, namespaces_options(@project.namespace_id, :all), {}, {class: 'chosen'}
|
||||||
|
%br
|
||||||
|
%ul.prepend-top-10.cred
|
||||||
|
%li Be careful. Changing project namespace can have unintended side effects
|
||||||
|
%li You can transfer project only to namespaces you can manage
|
||||||
|
%li You will need to update your local repositories to point to the new location.
|
||||||
|
|
||||||
|
|
||||||
|
.actions
|
||||||
|
= f.submit 'Save Project', class: "btn btn-save"
|
||||||
|
= link_to 'Cancel', admin_projects_path, class: "btn btn-cancel"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
:javascript
|
||||||
|
$(function(){
|
||||||
|
new Projects();
|
||||||
|
})
|
||||||
|
|
3
app/views/admin/projects/edit.html.haml
Normal file
3
app/views/admin/projects/edit.html.haml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
%h3.page_title #{@project.name} → Edit project
|
||||||
|
%hr
|
||||||
|
= render 'form', project: @project
|
|
@ -52,8 +52,8 @@
|
||||||
%i.icon-lock.cgreen
|
%i.icon-lock.cgreen
|
||||||
= link_to project.name_with_namespace, [:admin, project]
|
= link_to project.name_with_namespace, [:admin, project]
|
||||||
.pull-right
|
.pull-right
|
||||||
= link_to 'Edit', edit_project_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small"
|
= link_to 'Edit', edit_admin_project_path(project), id: "edit_#{dom_id(project)}", class: "btn btn-small"
|
||||||
= link_to 'Destroy', [project], confirm: "REMOVE #{project.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove"
|
= link_to 'Destroy', [:admin, project], confirm: "REMOVE #{project.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove"
|
||||||
- if @projects.blank?
|
- if @projects.blank?
|
||||||
%p.nothing_here_message 0 projects matches
|
%p.nothing_here_message 0 projects matches
|
||||||
- else
|
- else
|
||||||
|
|
|
@ -1,90 +1,129 @@
|
||||||
%h3.page_title
|
%h3.page_title
|
||||||
Project: #{@project.name_with_namespace}
|
Project: #{@project.name_with_namespace}
|
||||||
= link_to edit_project_path(@project), class: "btn pull-right" do
|
= link_to edit_admin_project_path(@project), class: "btn pull-right" do
|
||||||
%i.icon-edit
|
%i.icon-edit
|
||||||
Edit
|
Edit
|
||||||
%hr
|
|
||||||
.row
|
|
||||||
.span6
|
%br
|
||||||
.ui-box
|
%table.zebra-striped
|
||||||
%h5.title
|
%thead
|
||||||
Project info:
|
%tr
|
||||||
%ul.well-list
|
%th Project
|
||||||
%li
|
%th
|
||||||
%span.light Name:
|
%tr
|
||||||
%strong= @project.name
|
%td
|
||||||
%li
|
%b
|
||||||
%span.light Namespace:
|
Name:
|
||||||
%strong
|
%td
|
||||||
|
= @project.name
|
||||||
|
%tr
|
||||||
|
%td
|
||||||
|
%b
|
||||||
|
Namespace:
|
||||||
|
%td
|
||||||
- if @project.namespace
|
- if @project.namespace
|
||||||
= link_to @project.namespace.human_name, [:admin, @project.group || @project.owner]
|
= @project.namespace.human_name
|
||||||
- else
|
- else
|
||||||
Global
|
Global
|
||||||
%li
|
%tr
|
||||||
%span.light Owned by:
|
%td
|
||||||
%strong
|
%b
|
||||||
|
Owned by:
|
||||||
|
%td
|
||||||
- if @project.owner
|
- if @project.owner
|
||||||
= link_to @project.owner_name, admin_user_path(@project.owner)
|
= link_to @project.owner_name, admin_user_path(@project.owner)
|
||||||
- else
|
- else
|
||||||
(deleted)
|
(deleted)
|
||||||
|
%tr
|
||||||
%li
|
%td
|
||||||
%span.light Created by:
|
%b
|
||||||
%strong
|
Created by:
|
||||||
|
%td
|
||||||
= @project.creator.try(:name) || '(deleted)'
|
= @project.creator.try(:name) || '(deleted)'
|
||||||
|
%tr
|
||||||
%li
|
%td
|
||||||
%span.light Created at:
|
%b
|
||||||
%strong
|
Created at:
|
||||||
|
%td
|
||||||
= @project.created_at.stamp("March 1, 1999")
|
= @project.created_at.stamp("March 1, 1999")
|
||||||
|
%tr
|
||||||
%li
|
%td
|
||||||
%span.light http:
|
%b
|
||||||
%strong
|
Smart HTTP:
|
||||||
|
%td
|
||||||
= link_to @project.http_url_to_repo
|
= link_to @project.http_url_to_repo
|
||||||
%li
|
%tr
|
||||||
%span.light ssh:
|
%td
|
||||||
%strong
|
%b
|
||||||
|
SSH:
|
||||||
|
%td
|
||||||
= link_to @project.ssh_url_to_repo
|
= link_to @project.ssh_url_to_repo
|
||||||
- if @project.repository.exists?
|
|
||||||
%li
|
|
||||||
%span.light fs:
|
|
||||||
%strong
|
|
||||||
= @repository.path_to_repo
|
|
||||||
|
|
||||||
%li
|
|
||||||
%span.light last commit:
|
|
||||||
%strong
|
|
||||||
= last_commit(@project)
|
|
||||||
- else
|
|
||||||
%li
|
|
||||||
%span.light repository:
|
|
||||||
%strong.cred
|
|
||||||
does not exist
|
|
||||||
|
|
||||||
%li
|
|
||||||
%span.light access:
|
|
||||||
%strong
|
|
||||||
- if @project.public
|
- if @project.public
|
||||||
%span.cblue
|
%tr.bgred
|
||||||
%i.icon-share
|
%td
|
||||||
Public
|
%b
|
||||||
- else
|
Public Read-Only Code access:
|
||||||
%span.cgreen
|
%td
|
||||||
%i.icon-lock
|
= check_box_tag 'public', nil, @project.public
|
||||||
Private
|
|
||||||
.span6
|
- if @repository
|
||||||
.ui-box
|
%table.zebra-striped
|
||||||
%h5.title
|
%thead
|
||||||
|
%tr
|
||||||
|
%th Repository
|
||||||
|
%th
|
||||||
|
%tr
|
||||||
|
%td
|
||||||
|
%b
|
||||||
|
FS Path:
|
||||||
|
%td
|
||||||
|
%code= @repository.path_to_repo
|
||||||
|
%tr
|
||||||
|
%td
|
||||||
|
%b
|
||||||
|
Last commit at:
|
||||||
|
%td
|
||||||
|
= last_commit(@project)
|
||||||
|
|
||||||
|
%br
|
||||||
|
%h5
|
||||||
Team
|
Team
|
||||||
%small
|
%small
|
||||||
(#{@project.users.count})
|
(#{@project.users.count})
|
||||||
= link_to project_team_index_path(@project), class: "btn btn-tiny" do
|
%br
|
||||||
%i.icon-edit
|
%table.zebra-striped.team_members
|
||||||
Edit Team
|
%thead
|
||||||
%ul.well-list.team_members
|
%tr
|
||||||
|
%th Name
|
||||||
|
%th Project Access
|
||||||
|
%th Repository Access
|
||||||
|
%th
|
||||||
|
|
||||||
- @project.users.each do |tm|
|
- @project.users.each do |tm|
|
||||||
%li
|
%tr
|
||||||
%strong
|
%td
|
||||||
= link_to tm.name, admin_user_path(tm)
|
= link_to tm.name, admin_user_path(tm)
|
||||||
%span.pull-right.light= @project.project_access_human(tm)
|
%td= @project.project_access_human(tm)
|
||||||
|
%td= link_to 'Edit Access', edit_admin_project_member_path(@project, tm), class: "btn btn-small"
|
||||||
|
%td= link_to 'Remove from team', admin_project_member_path(@project, tm), confirm: 'Are you sure?', method: :delete, class: "btn btn-remove small"
|
||||||
|
|
||||||
|
%br
|
||||||
|
%h5 Add new team member
|
||||||
|
%br
|
||||||
|
= form_tag team_update_admin_project_path(@project), class: "bulk_import", method: :put do
|
||||||
|
%table.zebra-striped
|
||||||
|
%thead
|
||||||
|
%tr
|
||||||
|
%th Users
|
||||||
|
%th Project Access:
|
||||||
|
|
||||||
|
%tr
|
||||||
|
%td= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name), multiple: true, data: {placeholder: 'Select users'}, class: 'chosen span5'
|
||||||
|
%td= select_tag :project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3"}
|
||||||
|
|
||||||
|
%tr
|
||||||
|
%td= submit_tag 'Add', class: "btn btn-primary"
|
||||||
|
%td
|
||||||
|
Read more about project permissions
|
||||||
|
%strong= link_to "here", help_permissions_path, class: "vlink"
|
||||||
|
|
0
app/views/admin/projects/team.html.haml
Normal file
0
app/views/admin/projects/team.html.haml
Normal file
|
@ -6,7 +6,7 @@
|
||||||
%i.icon-angle-right
|
%i.icon-angle-right
|
||||||
= link_to project_tree_path(@project, @ref) do
|
= link_to project_tree_path(@project, @ref) do
|
||||||
= @project.name
|
= @project.name
|
||||||
- tree_breadcrumbs(@tree, 6) do |link|
|
- @tree.breadcrumbs(6) do |link|
|
||||||
\/
|
\/
|
||||||
%li= link
|
%li= link
|
||||||
.clear
|
.clear
|
||||||
|
@ -22,13 +22,13 @@
|
||||||
%table
|
%table
|
||||||
- current_line = 1
|
- current_line = 1
|
||||||
- @blame.each do |commit, lines|
|
- @blame.each do |commit, lines|
|
||||||
- commit = Commit.new(commit)
|
- commit = CommitDecorator.decorate(Commit.new(commit))
|
||||||
%tr
|
%tr
|
||||||
%td.blame-commit
|
%td.blame-commit
|
||||||
%span.commit
|
%span.commit
|
||||||
= link_to commit.short_id(8), project_commit_path(@project, commit), class: "commit_short_id"
|
= link_to commit.short_id(8), project_commit_path(@project, commit), class: "commit_short_id"
|
||||||
|
|
||||||
= commit_author_link(commit, avatar: true, size: 16)
|
= commit.author_link avatar: true, size: 16
|
||||||
|
|
||||||
= link_to_gfm truncate(commit.title, length: 20), project_commit_path(@project, commit.id), class: "row_title"
|
= link_to_gfm truncate(commit.title, length: 20), project_commit_path(@project, commit.id), class: "row_title"
|
||||||
%td.lines.blame-numbers
|
%td.lines.blame-numbers
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
= render "commit_box"
|
= render "commits/commit_box"
|
||||||
|
|
||||||
- unless @commit.has_zero_stats?
|
|
||||||
%p.pull-right.cgray
|
%p.pull-right.cgray
|
||||||
This commit has
|
This commit has
|
||||||
%span.cgreen #{@commit.stats.additions} additions
|
%span.cgreen #{@commit.stats.additions} additions
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
%strong= link_to "Browse Code »", project_tree_path(@project, commit), class: "right"
|
%strong= link_to "Browse Code »", project_tree_path(@project, commit), class: "right"
|
||||||
%p
|
%p
|
||||||
= link_to commit.short_id(8), project_commit_path(@project, commit), class: "commit_short_id"
|
= link_to commit.short_id(8), project_commit_path(@project, commit), class: "commit_short_id"
|
||||||
= commit_author_link(commit, avatar: true, size: 24)
|
= commit.author_link avatar: true, size: 24
|
||||||
|
|
||||||
= link_to_gfm truncate(commit.title, length: 70), project_commit_path(@project, commit.id), class: "row_title"
|
= link_to_gfm truncate(commit.title, length: 70), project_commit_path(@project, commit.id), class: "row_title"
|
||||||
|
|
||||||
|
|
|
@ -24,14 +24,14 @@
|
||||||
.row
|
.row
|
||||||
.span5
|
.span5
|
||||||
.author
|
.author
|
||||||
= commit_author_link(@commit, avatar: true, size: 32)
|
= @commit.author_link avatar: true, size: 32
|
||||||
authored
|
authored
|
||||||
%time{title: @commit.authored_date.stamp("Aug 21, 2011 9:23pm")}
|
%time{title: @commit.authored_date.stamp("Aug 21, 2011 9:23pm")}
|
||||||
#{time_ago_in_words(@commit.authored_date)} ago
|
#{time_ago_in_words(@commit.authored_date)} ago
|
||||||
- if @commit.different_committer?
|
- if @commit.different_committer?
|
||||||
.committer
|
.committer
|
||||||
→
|
→
|
||||||
= commit_committer_link(@commit)
|
= @commit.committer_link
|
||||||
committed
|
committed
|
||||||
%time{title: @commit.committed_date.stamp("Aug 21, 2011 9:23pm")}
|
%time{title: @commit.committed_date.stamp("Aug 21, 2011 9:23pm")}
|
||||||
#{time_ago_in_words(@commit.committed_date)} ago
|
#{time_ago_in_words(@commit.committed_date)} ago
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
- if @path.present?
|
- if @path.present?
|
||||||
%ul.breadcrumb
|
%ul.breadcrumb
|
||||||
= commits_breadcrumbs
|
= breadcrumbs
|
||||||
|
|
||||||
%div{id: dom_id(@project)}
|
%div{id: dom_id(@project)}
|
||||||
#commits-list= render "commits"
|
#commits-list= render "commits"
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
%div.ui-box
|
%div.ui-box
|
||||||
%h5.title
|
%h5.title
|
||||||
Commits (#{@commits.count})
|
Commits (#{@commits.count})
|
||||||
%ul.well-list= render Commit.decorate(@commits)
|
%ul.well-list= render @commits
|
||||||
|
|
||||||
- unless @diffs.empty?
|
- unless @diffs.empty?
|
||||||
%h4 Diff
|
%h4 Diff
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
.ui-box
|
.ui-box
|
||||||
%h5.title
|
%h5.title
|
||||||
Groups
|
Groups
|
||||||
%span.light
|
%small
|
||||||
(#{groups.count})
|
(#{groups.count})
|
||||||
- if current_user.can_create_group?
|
- if current_user.can_create_group?
|
||||||
%span.pull-right
|
%span.pull-right
|
||||||
= link_to new_group_path, class: "btn btn-small" do
|
= link_to new_group_path, class: "btn btn-tiny info" do
|
||||||
%i.icon-plus
|
%i.icon-plus
|
||||||
New Group
|
New Group
|
||||||
%ul.well-list
|
%ul.well-list
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
.ui-box
|
.ui-box
|
||||||
%h5.title
|
%h5.title
|
||||||
Projects
|
Projects
|
||||||
%span.light
|
%small
|
||||||
(#{@projects_count})
|
(#{@projects_count})
|
||||||
- if current_user.can_create_project?
|
- if current_user.can_create_project?
|
||||||
%span.pull-right
|
%span.pull-right
|
||||||
= link_to new_project_path, class: "btn btn-small" do
|
= link_to new_project_path, class: "btn btn-tiny info" do
|
||||||
%i.icon-plus
|
%i.icon-plus
|
||||||
New Project
|
New Project
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
.ui-box.teams-box
|
.ui-box.teams-box
|
||||||
%h5.title
|
%h5.title
|
||||||
Teams
|
Teams
|
||||||
%span.light
|
%small
|
||||||
(#{teams.count})
|
(#{teams.count})
|
||||||
%span.pull-right
|
%span.pull-right
|
||||||
= link_to new_team_path, class: "btn btn-small" do
|
= link_to new_team_path, class: "btn btn-tiny info" do
|
||||||
%i.icon-plus
|
%i.icon-plus
|
||||||
New Team
|
New Team
|
||||||
%ul.well-list
|
%ul.well-list
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
- if ldap_enable?
|
- if ldap_enable?
|
||||||
= render partial: 'devise/sessions/new_ldap'
|
= render :partial => 'devise/sessions/new_ldap'
|
||||||
- else
|
- else
|
||||||
= form_for(resource, as: resource_name, url: session_path(resource_name), html: { class: "login-box" }) do |f|
|
= form_for(resource, :as => resource_name, :url => session_path(resource_name), :html => { :class => "login-box" }) do |f|
|
||||||
= image_tag "login-logo.png", width: "304", height: "66", class: "login-logo", alt: "Login Logo"
|
= image_tag "login-logo.png", :width => "304", :height => "66", :class => "login-logo", :alt => "Login Logo"
|
||||||
= f.text_field :login, class: "text top", placeholder: "Username or Email", autofocus: "autofocus"
|
= f.email_field :email, :class => "text top", :placeholder => "Email", :autofocus => "autofocus"
|
||||||
= f.password_field :password, class: "text bottom", placeholder: "Password"
|
= f.password_field :password, :class => "text bottom", :placeholder => "Password"
|
||||||
- if devise_mapping.rememberable?
|
- if devise_mapping.rememberable?
|
||||||
.clearfix.inputs-list
|
.clearfix.inputs-list
|
||||||
%label.checkbox.remember_me{for: "user_remember_me"}
|
%label.checkbox.remember_me{:for => "user_remember_me"}
|
||||||
= f.check_box :remember_me
|
= f.check_box :remember_me
|
||||||
%span Remember me
|
%span Remember me
|
||||||
%br/
|
%br/
|
||||||
= f.submit "Sign in", class: "btn-create btn"
|
= f.submit "Sign in", :class => "btn-create btn"
|
||||||
.pull-right
|
.pull-right
|
||||||
= link_to "Forgot your password?", new_password_path(resource_name), class: "btn"
|
= link_to "Forgot your password?", new_password_path(resource_name), :class => "btn"
|
||||||
%br/
|
%br/
|
||||||
- if Gitlab.config.gitlab.signup_enabled
|
- if Gitlab.config.gitlab.signup_enabled
|
||||||
%hr/
|
%hr/
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
- commit = CommitDecorator.decorate(commit)
|
||||||
%li.commit
|
%li.commit
|
||||||
%p
|
%p
|
||||||
= link_to commit.short_id(8), project_commit_path(project, commit), class: "commit_short_id"
|
= link_to commit.short_id(8), project_commit_path(project, commit), class: "commit_short_id"
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
},
|
},
|
||||||
time: c.time,
|
time: c.time,
|
||||||
space: c.spaces.first,
|
space: c.spaces.first,
|
||||||
refs: get_refs(c),
|
refs: join_with_space(c.refs),
|
||||||
id: c.sha,
|
id: c.sha,
|
||||||
date: c.date,
|
date: c.date,
|
||||||
message: c.message,
|
message: c.message,
|
||||||
|
|
|
@ -4,8 +4,24 @@
|
||||||
%body{class: "#{app_theme} admin"}
|
%body{class: "#{app_theme} admin"}
|
||||||
= render "layouts/head_panel", title: "Admin area"
|
= render "layouts/head_panel", title: "Admin area"
|
||||||
= render "layouts/flash"
|
= render "layouts/flash"
|
||||||
%nav.main-nav
|
|
||||||
.container= render 'layouts/nav/admin'
|
|
||||||
|
|
||||||
.container
|
.container
|
||||||
|
%ul.main_menu
|
||||||
|
= nav_link(controller: :dashboard, html_options: {class: 'home'}) do
|
||||||
|
= link_to admin_root_path, title: "Stats" do
|
||||||
|
%i.icon-home
|
||||||
|
= nav_link(controller: :projects) do
|
||||||
|
= link_to "Projects", admin_projects_path
|
||||||
|
= nav_link(controller: :teams) do
|
||||||
|
= link_to "Teams", admin_teams_path
|
||||||
|
= nav_link(controller: :groups) do
|
||||||
|
= link_to "Groups", admin_groups_path
|
||||||
|
= nav_link(controller: :users) do
|
||||||
|
= link_to "Users", admin_users_path
|
||||||
|
= nav_link(controller: :logs) do
|
||||||
|
= link_to "Logs", admin_logs_path
|
||||||
|
= nav_link(controller: :hooks) do
|
||||||
|
= link_to "Hooks", admin_hooks_path
|
||||||
|
= nav_link(controller: :resque) do
|
||||||
|
= link_to "Background Jobs", admin_resque_path
|
||||||
|
|
||||||
.content= yield
|
.content= yield
|
||||||
|
|
|
@ -4,8 +4,25 @@
|
||||||
%body{class: "#{app_theme} application"}
|
%body{class: "#{app_theme} application"}
|
||||||
= render "layouts/head_panel", title: "Dashboard"
|
= render "layouts/head_panel", title: "Dashboard"
|
||||||
= render "layouts/flash"
|
= render "layouts/flash"
|
||||||
%nav.main-nav
|
|
||||||
.container= render 'layouts/nav/dashboard'
|
|
||||||
|
|
||||||
.container
|
.container
|
||||||
|
%ul.main_menu
|
||||||
|
= nav_link(path: 'dashboard#show', html_options: {class: 'home'}) do
|
||||||
|
= link_to root_path, title: "Home" do
|
||||||
|
%i.icon-home
|
||||||
|
= nav_link(path: 'dashboard#projects') do
|
||||||
|
= link_to projects_dashboard_path do
|
||||||
|
Projects
|
||||||
|
= nav_link(path: 'dashboard#issues') do
|
||||||
|
= link_to issues_dashboard_path do
|
||||||
|
Issues
|
||||||
|
%span.count= current_user.assigned_issues.opened.count
|
||||||
|
= nav_link(path: 'dashboard#merge_requests') do
|
||||||
|
= link_to merge_requests_dashboard_path do
|
||||||
|
Merge Requests
|
||||||
|
%span.count= current_user.cared_merge_requests.opened.count
|
||||||
|
= nav_link(path: 'search#show') do
|
||||||
|
= link_to "Search", search_path
|
||||||
|
= nav_link(controller: :help) do
|
||||||
|
= link_to "Help", help_path
|
||||||
|
|
||||||
.content= yield
|
.content= yield
|
||||||
|
|
|
@ -4,8 +4,25 @@
|
||||||
%body{class: "#{app_theme} application"}
|
%body{class: "#{app_theme} application"}
|
||||||
= render "layouts/head_panel", title: "group: #{@group.name}"
|
= render "layouts/head_panel", title: "group: #{@group.name}"
|
||||||
= render "layouts/flash"
|
= render "layouts/flash"
|
||||||
%nav.main-nav
|
|
||||||
.container= render 'layouts/nav/group'
|
|
||||||
|
|
||||||
.container
|
.container
|
||||||
|
%ul.main_menu
|
||||||
|
= nav_link(path: 'groups#show', html_options: {class: 'home'}) do
|
||||||
|
= link_to group_path(@group), title: "Home" do
|
||||||
|
%i.icon-home
|
||||||
|
= nav_link(path: 'groups#issues') do
|
||||||
|
= link_to issues_group_path(@group) do
|
||||||
|
Issues
|
||||||
|
%span.count= current_user.assigned_issues.opened.of_group(@group).count
|
||||||
|
= nav_link(path: 'groups#merge_requests') do
|
||||||
|
= link_to merge_requests_group_path(@group) do
|
||||||
|
Merge Requests
|
||||||
|
%span.count= current_user.cared_merge_requests.opened.of_group(@group).count
|
||||||
|
= nav_link(path: 'groups#people') do
|
||||||
|
= link_to "People", people_group_path(@group)
|
||||||
|
|
||||||
|
- if can?(current_user, :manage_group, @group)
|
||||||
|
= nav_link(path: 'groups#edit') do
|
||||||
|
= link_to edit_group_path(@group), class: "tab " do
|
||||||
|
Settings
|
||||||
|
|
||||||
.content= yield
|
.content= yield
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
%ul
|
|
||||||
= nav_link(controller: :dashboard, html_options: {class: 'home'}) do
|
|
||||||
= link_to admin_root_path, title: "Stats" do
|
|
||||||
%i.icon-home
|
|
||||||
= nav_link(controller: :projects) do
|
|
||||||
= link_to "Projects", admin_projects_path
|
|
||||||
= nav_link(controller: :teams) do
|
|
||||||
= link_to "Teams", admin_teams_path
|
|
||||||
= nav_link(controller: :groups) do
|
|
||||||
= link_to "Groups", admin_groups_path
|
|
||||||
= nav_link(controller: :users) do
|
|
||||||
= link_to "Users", admin_users_path
|
|
||||||
= nav_link(controller: :logs) do
|
|
||||||
= link_to "Logs", admin_logs_path
|
|
||||||
= nav_link(controller: :hooks) do
|
|
||||||
= link_to "Hooks", admin_hooks_path
|
|
||||||
= nav_link(controller: :resque) do
|
|
||||||
= link_to "Background Jobs", admin_resque_path
|
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
%ul
|
|
||||||
= nav_link(path: 'dashboard#show', html_options: {class: 'home'}) do
|
|
||||||
= link_to root_path, title: "Home" do
|
|
||||||
%i.icon-home
|
|
||||||
= nav_link(path: 'dashboard#projects') do
|
|
||||||
= link_to projects_dashboard_path do
|
|
||||||
Projects
|
|
||||||
= nav_link(path: 'dashboard#issues') do
|
|
||||||
= link_to issues_dashboard_path do
|
|
||||||
Issues
|
|
||||||
%span.count= current_user.assigned_issues.opened.count
|
|
||||||
= nav_link(path: 'dashboard#merge_requests') do
|
|
||||||
= link_to merge_requests_dashboard_path do
|
|
||||||
Merge Requests
|
|
||||||
%span.count= current_user.cared_merge_requests.opened.count
|
|
||||||
= nav_link(path: 'search#show') do
|
|
||||||
= link_to "Search", search_path
|
|
||||||
= nav_link(controller: :help) do
|
|
||||||
= link_to "Help", help_path
|
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
%ul
|
|
||||||
= nav_link(path: 'groups#show', html_options: {class: 'home'}) do
|
|
||||||
= link_to group_path(@group), title: "Home" do
|
|
||||||
%i.icon-home
|
|
||||||
= nav_link(path: 'groups#issues') do
|
|
||||||
= link_to issues_group_path(@group) do
|
|
||||||
Issues
|
|
||||||
%span.count= current_user.assigned_issues.opened.of_group(@group).count
|
|
||||||
= nav_link(path: 'groups#merge_requests') do
|
|
||||||
= link_to merge_requests_group_path(@group) do
|
|
||||||
Merge Requests
|
|
||||||
%span.count= current_user.cared_merge_requests.opened.of_group(@group).count
|
|
||||||
= nav_link(path: 'groups#people') do
|
|
||||||
= link_to "People", people_group_path(@group)
|
|
||||||
|
|
||||||
- if can?(current_user, :manage_group, @group)
|
|
||||||
= nav_link(path: 'groups#edit') do
|
|
||||||
= link_to edit_group_path(@group), class: "tab " do
|
|
||||||
Settings
|
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
%ul
|
|
||||||
= nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
|
|
||||||
= link_to profile_path, title: "Profile" do
|
|
||||||
%i.icon-home
|
|
||||||
= nav_link(path: 'profiles#account') do
|
|
||||||
= link_to "Account", account_profile_path
|
|
||||||
= nav_link(controller: :notifications) do
|
|
||||||
= link_to "Notifications", profile_notifications_path
|
|
||||||
= nav_link(controller: :keys) do
|
|
||||||
= link_to keys_path do
|
|
||||||
SSH Keys
|
|
||||||
%span.count= current_user.keys.count
|
|
||||||
= nav_link(path: 'profiles#design') do
|
|
||||||
= link_to "Design", design_profile_path
|
|
||||||
= nav_link(path: 'profiles#history') do
|
|
||||||
= link_to "History", history_profile_path
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue