summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG11
-rw-r--r--CONTRIBUTING.md25
-rw-r--r--Gemfile14
-rw-r--r--Gemfile.lock38
-rw-r--r--README.md6
-rw-r--r--VERSION2
-rw-r--r--app/assets/javascripts/dispatcher.js.coffee4
-rw-r--r--app/assets/javascripts/milestones.js.coffee14
-rw-r--r--app/assets/javascripts/notes.js147
-rw-r--r--app/assets/stylesheets/gitlab_bootstrap/avatar.scss1
-rw-r--r--app/assets/stylesheets/gitlab_bootstrap/nav.scss12
-rw-r--r--app/assets/stylesheets/sections/events.scss1
-rw-r--r--app/assets/stylesheets/sections/header.scss1
-rw-r--r--app/assets/stylesheets/sections/issues.scss4
-rw-r--r--app/assets/stylesheets/sections/notes.scss33
-rw-r--r--app/contexts/issues/list_context.rb2
-rw-r--r--app/contexts/merge_requests_load_context.rb2
-rw-r--r--app/contexts/projects/create_context.rb1
-rw-r--r--app/controllers/admin/groups_controller.rb4
-rw-r--r--app/controllers/admin/teams/projects_controller.rb2
-rw-r--r--app/controllers/admin/users_controller.rb8
-rw-r--r--app/controllers/application_controller.rb7
-rw-r--r--app/controllers/notes_controller.rb26
-rw-r--r--app/controllers/passwords_controller.rb38
-rw-r--r--app/controllers/profiles_controller.rb15
-rw-r--r--app/controllers/snippets_controller.rb8
-rw-r--r--app/helpers/application_helper.rb2
-rw-r--r--app/helpers/issues_helper.rb2
-rw-r--r--app/helpers/notes_helper.rb7
-rw-r--r--app/helpers/projects_helper.rb38
-rw-r--r--app/models/concerns/issuable.rb19
-rw-r--r--app/models/concerns/mentionable.rb37
-rw-r--r--app/models/issue.rb2
-rw-r--r--app/models/key.rb2
-rw-r--r--app/models/merge_request.rb9
-rw-r--r--app/models/note.rb2
-rw-r--r--app/models/project.rb6
-rw-r--r--app/models/user.rb21
-rw-r--r--app/observers/project_observer.rb16
-rw-r--r--app/services/notification_service.rb15
-rw-r--r--app/views/admin/hooks/_data_ex.html.erb29
-rw-r--r--app/views/admin/teams/members/new.html.haml54
-rw-r--r--app/views/admin/teams/projects/new.html.haml2
-rw-r--r--app/views/admin/users/_form.html.haml30
-rw-r--r--app/views/admin/users/show.html.haml114
-rw-r--r--app/views/dashboard/projects.html.haml28
-rw-r--r--app/views/devise/sessions/_oauth_providers.html.haml5
-rw-r--r--app/views/edit_tree/show.html.haml2
-rw-r--r--app/views/events/_commit.html.haml2
-rw-r--r--app/views/events/_event.html.haml2
-rw-r--r--app/views/groups/people.html.haml2
-rw-r--r--app/views/help/index.html.haml2
-rw-r--r--app/views/help/markdown.html.haml126
-rw-r--r--app/views/issues/_issues.html.haml6
-rw-r--r--app/views/issues/show.html.haml5
-rw-r--r--app/views/layouts/_head_panel.html.haml3
-rw-r--r--app/views/layouts/nav/_project.html.haml39
-rw-r--r--app/views/layouts/nav/_team.html.haml2
-rw-r--r--app/views/layouts/snippets.html.haml20
-rw-r--r--app/views/milestones/_issues.html.haml11
-rw-r--r--app/views/milestones/_merge_request.html.haml5
-rw-r--r--app/views/milestones/show.html.haml79
-rw-r--r--app/views/notes/_discussion.html.haml2
-rw-r--r--app/views/notes/_note.html.haml51
-rw-r--r--app/views/notify/new_user_email.html.haml9
-rw-r--r--app/views/notify/new_user_email.text.erb5
-rw-r--r--app/views/passwords/new.html.haml22
-rw-r--r--app/views/profiles/account.html.haml2
-rw-r--r--app/views/profiles/show.html.haml2
-rw-r--r--app/views/projects/_settings_nav.html.haml6
-rw-r--r--app/views/projects/teams/available.html.haml2
-rw-r--r--app/views/repositories/_branch.html.haml2
-rw-r--r--app/views/repositories/_feed.html.haml2
-rw-r--r--app/views/repositories/stats.html.haml2
-rw-r--r--app/views/snippets/_blob.html.haml1
-rw-r--r--app/views/snippets/_snippet.html.haml2
-rw-r--r--app/views/snippets/current_user_index.html.haml7
-rw-r--r--app/views/snippets/index.html.haml8
-rw-r--r--app/views/snippets/show.html.haml4
-rw-r--r--app/views/team_members/_team_member.html.haml2
-rw-r--r--app/views/team_members/index.html.haml2
-rw-r--r--app/views/teams/edit.html.haml5
-rw-r--r--app/views/teams/members/_member.html.haml2
-rw-r--r--app/views/users/show.html.haml2
-rw-r--r--config/environments/production.rb2
-rw-r--r--config/gitlab.yml.example9
-rw-r--r--config/puma.rb.example7
-rw-r--r--config/routes.rb6
-rw-r--r--config/unicorn.rb.example102
-rw-r--r--db/fixtures/development/09_issues.rb2
-rw-r--r--db/fixtures/development/10_merge_requests.rb3
-rw-r--r--db/fixtures/production/001_admin.rb3
-rw-r--r--db/migrate/20130326142630_add_index_to_users_authentication_token.rb5
-rw-r--r--db/migrate/20130613165816_add_password_expires_at_to_users.rb5
-rw-r--r--db/migrate/20130613173246_add_created_by_id_to_user.rb5
-rw-r--r--db/migrate/20130614132337_add_improted_to_project.rb5
-rw-r--r--db/schema.rb6
-rw-r--r--doc/api/README.md29
-rw-r--r--doc/api/projects.md25
-rw-r--r--doc/install/databases.md19
-rw-r--r--doc/install/installation.md93
-rw-r--r--doc/markdown/markdown.md451
-rw-r--r--doc/update/5.0-to-5.1.md1
-rw-r--r--doc/update/5.1-to-5.2.md15
-rw-r--r--doc/update/5.2-to-5.3.md80
-rw-r--r--features/project/issues/milestones.feature2
-rw-r--r--features/steps/project/project_active_tab.rb8
-rw-r--r--features/steps/project/project_merge_requests.rb4
-rw-r--r--features/steps/project/project_milestones.rb8
-rw-r--r--features/steps/userteams/userteams.rb30
-rw-r--r--lib/api/entities.rb7
-rw-r--r--lib/api/helpers.rb6
-rw-r--r--lib/api/projects.rb36
-rw-r--r--lib/gitlab/backend/grack_auth.rb141
-rw-r--r--lib/gitlab/backend/grack_helpers.rb28
-rw-r--r--lib/gitlab/backend/grack_ldap.rb24
-rw-r--r--lib/gitlab/blacklist.rb2
-rw-r--r--lib/redcarpet/render/gitlab_html.rb2
-rw-r--r--lib/support/init.d/gitlab2
-rw-r--r--lib/support/nginx/gitlab2
-rw-r--r--lib/tasks/gitlab/import.rake2
-rw-r--r--spec/factories.rb7
-rw-r--r--spec/features/admin/admin_users_spec.rb18
-rw-r--r--spec/features/notes_on_merge_requests_spec.rb68
-rw-r--r--spec/fixtures/dk.pngbin0 -> 1143 bytes
-rw-r--r--spec/mailers/notify_spec.rb8
-rw-r--r--spec/requests/api/projects_spec.rb67
-rw-r--r--spec/routing/project_routing_spec.rb2
-rw-r--r--spec/services/notification_service_spec.rb5
129 files changed, 1995 insertions, 671 deletions
diff --git a/CHANGELOG b/CHANGELOG
index f26466f9da8..c20f27ad7db 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,13 @@
+v 5.4.0
+ - Ability to edit own comments
+ - Documentation improvements
+ - Improve dashboard projects page
+ - Fixed nav for empty repos
+ - GitLab Markdown help page
+ - Misspelling fixes
+ - Added suppoort of unicorn and fog gems
+ - Added client list to API doc
+
v 5.3.0
- Refactored services
- Campfire service added
@@ -24,6 +34,7 @@ v 5.3.0
- init.d: Ensure socket is removed before starting service
- Admin area: Style teams:index, group:show pages
- Own page for failed forking
+ - Scrum view for milestone
v 5.2.0
- Turbolinks
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 9084d99db3a..2a6eb71b654 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -4,25 +4,31 @@ This guide details how to use issues and pull requests to improve GitLab.
## Closing policy for issues and pull requests
-Issues and pull requests not in line with the guidelines listed in this document will be closed. 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/).
+GitLab is a popular open source project and the capacity to deal with issues and pull requests is limited. Out of respect for our volunteers, issues and pull requests not in line with the guidelines listed in this document may be closed without notice.
+
+Please treat our volunteers with courtesy and respect, it will go a long way towards getting your issue resolved.
+
+Issues and pull requests should be in English and contain appropriate language for audiences of all ages.
## Issue tracker
-The [issue tracker](https://github.com/gitlabhq/gitlabhq/issues) is only for obvious bugs or misbehavior in the latest [stable or development release of GitLab](MAINTENANCE.md). When submitting an issue please conform to the issue submission guidelines listed below.
+To get support for your particular problem please use the 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/).
+
+The [issue tracker](https://github.com/gitlabhq/gitlabhq/issues) is only for obvious bugs or misbehavior in the latest [stable or development release of GitLab](MAINTENANCE.md). When submitting an issue please conform to the issue submission guidelines listed below. Not all issues will be addressed and your issue is more likely to be addressed if you submit a pull request which partially or fully addresses the issue.
Do not use the issue tracker for feature requests. We have a specific [feedback and suggestions forum](http://feedback.gitlab.com) for this purpose.
-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) or [Stack Overflow](http://stackoverflow.com/questions/tagged/gitlab) 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.
+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 [mailing list](https://groups.google.com/forum/#!forum/gitlabhq) or [Stack Overflow](http://stackoverflow.com/questions/tagged/gitlab) 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.
### Issue tracker guidelines
-**[Search](https://github.com/gitlabhq/gitlabhq/search?q=&ref=cmdform&type=Issues)** for similar entries before submitting your own, there's a good chance somebody else had the same issue. Show your support with `:+1:` and/or join the discussion. Please submit issues in the following format:
+**[Search](https://github.com/gitlabhq/gitlabhq/search?q=&ref=cmdform&type=Issues)** for similar entries before submitting your own, there's a good chance somebody else had the same issue. Show your support with `:+1:` and/or join the discussion. Please submit issues in the following format (as the first post):
1. **Summary:** Summarize your issue in one sentence (what goes wrong, what did you expect to happen)
2. **Steps to reproduce:** How can we reproduce the issue, preferably on the [GitLab Vagrant virtual machine](https://github.com/gitlabhq/gitlab-vagrant-vm) (start with: `vagrant destroy && vagrant up && vagrant ssh`)
3. **Expected behavior:** Describe your issue in detail
4. **Observed behavior**
-5. **Relevant logs and/or screen shots:** Please use code blocks (\`\`\`) to format console output, logs, and code as it's very hard to read otherwise.
+5. **Relevant logs and/or screenshots:** Please use code blocks (\`\`\`) to format console output, logs, and code as it's very hard to read otherwise.
6. **Output of checks**
* Results of GitLab [Application Check](doc/install/installation.md#check-application-status) (`sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production`); we will only investigate if the tests are passing
* Version of GitLab you are running; we will only investigate issues in the latest stable and development releases as per the [maintenance policy](MAINTENANCE.md)
@@ -36,11 +42,12 @@ We welcome pull requests with fixes and improvements to GitLab code, tests, and/
### Pull request guidelines
-If you can, please submit a pull request with the fix or improvements including tests. If you don't know how to fix the issue but can write a test that exposes the issue we will accept that as well. The workflow to make a pull request is as follows:
+If you can, please submit a pull request with the fix or improvements including tests. If you don't know how to fix the issue but can write a test that exposes the issue we will accept that as well. In general bug fixes that include a regression test are merged quickly while new features without proper tests are least likely to receive timely feedback. The workflow to make a pull request is as follows:
1. Fork the project on GitHub
1. Create a feature branch
1. Write [tests](README.md#run-the-tests) and code
+1. Add your changes to the [CHANGELOG](CHANGELOG)
1. If you have multiple commits please combine them into one commit by [squashing them](http://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)
1. Push the commit to your fork
1. Submit a pull request
@@ -50,11 +57,13 @@ We will accept pull requests if:
* The code has proper tests and all tests pass (or it is a test exposing a failure in existing code)
* It can be merged without problems (if not please use: `git rebase master`)
-* It doesn't break any existing functionality
-* It's quality code that conforms to the [Rails style guide](https://github.com/bbatsov/rails-style-guide) and best practices
+* It does not break any existing functionality
+* It's quality code that conforms to the [Ruby](https://github.com/bbatsov/ruby-style-guide) and [Rails](https://github.com/bbatsov/rails-style-guide) style guides and best practices
* The description includes a motive for your change and the method you used to achieve it
+* It is not a catch all pull request but rather fixes a specific issue or implements a specific feature
* It keeps the GitLab code base clean and well structured
* We think other users will benefit from the same functionality
* If it makes changes to the UI the pull request should include screenshots
+* It is a single commit (please use `git rebase -i` to squash commits)
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).
diff --git a/Gemfile b/Gemfile
index e4206ab3d0d..03d3f5c1c23 100644
--- a/Gemfile
+++ b/Gemfile
@@ -29,7 +29,7 @@ gem 'gitlab_git', '~> 1.3.0'
gem 'gitlab-grack', '~> 1.0.1', require: 'grack'
# LDAP Auth
-gem 'gitlab_omniauth-ldap', '1.0.2', require: "omniauth-ldap"
+gem 'gitlab_omniauth-ldap', '1.0.3', require: "omniauth-ldap"
# Syntax highlighter
gem "gitlab-pygments.rb", '~> 0.3.2', require: 'pygments.rb'
@@ -59,8 +59,9 @@ gem "haml-rails"
# Files attachments
gem "carrierwave"
+
# for aws storage
-# gem "fog", "~> 1.3.1"
+gem "fog", "~> 1.3.1", group: :aws
# Authorization
gem "six"
@@ -72,8 +73,12 @@ gem "seed-fu"
gem "redcarpet", "~> 2.2.2"
gem "github-markup", "~> 0.7.4", require: 'github/markup'
+# Asciidoc to HTML
+gem "asciidoctor"
+
# Servers
-gem "puma", '~> 2.0.1'
+gem "puma", '~> 2.3.1', group: :puma
+gem "unicorn", '~> 4.6.3', group: :unicorn
# State machine
gem "state_machine"
@@ -113,6 +118,9 @@ gem "d3_rails", "~> 3.1.4"
# underscore-rails
gem "underscore-rails", "~> 1.4.4"
+# Sanitize user input
+gem "sanitize"
+
group :assets do
gem "sass-rails"
gem "coffee-rails"
diff --git a/Gemfile.lock b/Gemfile.lock
index b986539da1e..c26eeede69f 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -46,6 +46,7 @@ GEM
rails (~> 3.0)
addressable (2.3.4)
arel (3.0.2)
+ asciidoctor (0.1.3)
awesome_print (1.1.0)
backports (2.6.7)
bcrypt-ruby (3.0.1)
@@ -115,6 +116,7 @@ GEM
erubis (2.7.0)
escape_utils (0.2.4)
eventmachine (1.0.3)
+ excon (0.13.4)
execjs (1.4.0)
multi_json (~> 1.0)
factory_girl (4.2.0)
@@ -130,6 +132,16 @@ GEM
eventmachine (>= 0.12.0)
ffaker (1.16.0)
ffi (1.8.1)
+ fog (1.3.1)
+ builder
+ excon (~> 0.13.0)
+ formatador (~> 0.2.0)
+ mime-types
+ multi_json (~> 1.0)
+ net-scp (~> 1.0.4)
+ net-ssh (>= 2.1.3)
+ nokogiri (~> 1.5.0)
+ ruby-hmac
font-awesome-rails (3.1.1.3)
railties (>= 3.2, < 5.0)
foreman (0.63.0)
@@ -168,8 +180,8 @@ GEM
github-linguist (~> 2.3.4)
gitlab-grit (~> 2.5.1)
gitlab_meta (5.0)
- gitlab_omniauth-ldap (1.0.2)
- net-ldap (~> 0.2.2)
+ gitlab_omniauth-ldap (1.0.3)
+ net-ldap (~> 0.3.1)
omniauth (~> 1.0)
pyu-ruby-sasl (~> 0.0.3.1)
rubyntlm (~> 0.1.1)
@@ -242,6 +254,7 @@ GEM
kaminari (0.14.1)
actionpack (>= 3.0.0)
activesupport (>= 3.0.0)
+ kgio (2.8.0)
launchy (2.2.0)
addressable (~> 2.3)
letter_opener (1.1.0)
@@ -265,7 +278,10 @@ GEM
multi_xml (0.5.3)
multipart-post (1.2.0)
mysql2 (0.3.11)
- net-ldap (0.2.2)
+ net-ldap (0.3.1)
+ net-scp (1.0.4)
+ net-ssh (>= 1.99.1)
+ net-ssh (2.6.8)
nokogiri (1.5.9)
oauth (0.4.7)
oauth2 (0.8.1)
@@ -304,7 +320,7 @@ GEM
coderay (~> 1.0.5)
method_source (~> 0.8)
slop (~> 3.4)
- puma (2.0.1)
+ puma (2.3.1)
rack (>= 1.1, < 2.0)
pygments.rb (0.4.2)
posix-spawn (~> 0.3.6)
@@ -353,6 +369,7 @@ GEM
rake (>= 0.8.7)
rdoc (~> 3.4)
thor (>= 0.14.6, < 2.0)
+ raindrops (0.11.0)
rake (10.0.4)
rb-fsevent (0.9.3)
rb-inotify (0.9.0)
@@ -399,6 +416,7 @@ GEM
rspec-core (~> 2.13.0)
rspec-expectations (~> 2.13.0)
rspec-mocks (~> 2.13.0)
+ ruby-hmac (0.4.0)
ruby-progressbar (1.0.2)
rubyntlm (0.1.1)
rubyzip (0.9.9)
@@ -498,6 +516,10 @@ GEM
execjs (>= 0.3.0)
multi_json (~> 1.0, >= 1.0.2)
underscore-rails (1.4.4)
+ unicorn (4.6.3)
+ kgio (~> 2.6)
+ rack
+ raindrops (~> 0.7)
virtus (0.5.4)
backports (~> 2.6.1)
descendants_tracker (~> 0.0.1)
@@ -517,6 +539,7 @@ PLATFORMS
DEPENDENCIES
acts-as-taggable-on
annotate!
+ asciidoctor
awesome_print
better_errors
binding_of_caller
@@ -534,6 +557,7 @@ DEPENDENCIES
enumerize
factory_girl_rails
ffaker
+ fog (~> 1.3.1)
font-awesome-rails (~> 3.1.1)
foreman
gemoji (~> 1.2.1)
@@ -544,7 +568,7 @@ DEPENDENCIES
gitlab-pygments.rb (~> 0.3.2)
gitlab_git (~> 1.3.0)
gitlab_meta (= 5.0)
- gitlab_omniauth-ldap (= 1.0.2)
+ gitlab_omniauth-ldap (= 1.0.3)
gon
grape (~> 0.4.1)
grape-entity (~> 0.3.0)
@@ -572,7 +596,7 @@ DEPENDENCIES
pg
poltergeist (~> 1.3.0)
pry
- puma (~> 2.0.1)
+ puma (~> 2.3.1)
quiet_assets (~> 1.0.1)
rack-mini-profiler
rails (= 3.2.13)
@@ -584,6 +608,7 @@ DEPENDENCIES
redcarpet (~> 2.2.2)
redis-rails
rspec-rails
+ sanitize
sass-rails
sdoc
seed-fu
@@ -606,4 +631,5 @@ DEPENDENCIES
turbolinks
uglifier
underscore-rails (~> 1.4.4)
+ unicorn (~> 4.6.3)
webmock
diff --git a/README.md b/README.md
index 600fcff6d49..de773cce0eb 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,8 @@
![logo](https://raw.github.com/gitlabhq/gitlabhq/master/public/gitlab_logo.png)
+![animated-screenshots](http://makeagif.com/media/6-23-2013/AN3Mo6.gif)
+
### GitLab allows you to
* keep your code secure on your own server
* manage repositories, users and access permissions
@@ -22,7 +24,7 @@
* [![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.png)](https://codeclimate.com/github/gitlabhq/gitlabhq)
-* [![Dependency Status](https://gemnasium.com/gitlabhq/gitlabhq.png)](https://gemnasium.com/gitlabhq/gitlabhq) this button can be yellow (small updates are available) but must not be red (a security fix or an important update is available)
+* [![Dependency Status](https://gemnasium.com/gitlabhq/gitlabhq.png)](https://gemnasium.com/gitlabhq/gitlabhq) this button can be yellow (small updates are available) but must not be red (a security fix or an important update is available), gems are updated in major releases of GitLab.
* [![Coverage Status](https://coveralls.io/repos/gitlabhq/gitlabhq/badge.png?branch=master)](https://coveralls.io/r/gitlabhq/gitlabhq)
@@ -143,7 +145,7 @@ or start each component separately
* [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) and [Stack Overflow](http://stackoverflow.com/questions/tagged/gitlab) are the best places 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 has resolved it. 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.
+* [Mailing list](https://groups.google.com/forum/#!forum/gitlabhq) and [Stack Overflow](http://stackoverflow.com/questions/tagged/gitlab) are the best places 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 has resolved it. 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://feedback.gitlab.com) is the place to propose and discuss new features for GitLab.
diff --git a/VERSION b/VERSION
index 455f90e3b18..c5e18371adf 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-5.3.0.beta1
+5.4.0.pre
diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee
index fb149b7f677..130db5bfdbb 100644
--- a/app/assets/javascripts/dispatcher.js.coffee
+++ b/app/assets/javascripts/dispatcher.js.coffee
@@ -1,6 +1,6 @@
$ ->
new Dispatcher()
-
+
class Dispatcher
constructor: () ->
@initSearch()
@@ -10,8 +10,6 @@ class Dispatcher
page = $('body').attr('data-page')
project_id = $('body').attr('data-project-id')
- console.log(page)
-
unless page
return false
diff --git a/app/assets/javascripts/milestones.js.coffee b/app/assets/javascripts/milestones.js.coffee
deleted file mode 100644
index 99a52bf4d3f..00000000000
--- a/app/assets/javascripts/milestones.js.coffee
+++ /dev/null
@@ -1,14 +0,0 @@
-$ ->
- $('.milestone-issue-filter li[data-closed]').addClass('hide')
-
- $('.milestone-issue-filter ul.nav li a').click ->
- $('.milestone-issue-filter li').toggleClass('active')
- $('.milestone-issue-filter li[data-closed]').toggleClass('hide')
- false
-
- $('.milestone-merge-requests-filter li[data-closed]').addClass('hide')
-
- $('.milestone-merge-requests-filter ul.nav li a').click ->
- $('.milestone-merge-requests-filter li').toggleClass('active')
- $('.milestone-merge-requests-filter li[data-closed]').toggleClass('hide')
- false
diff --git a/app/assets/javascripts/notes.js b/app/assets/javascripts/notes.js
index f5005ec2c94..62961b529fd 100644
--- a/app/assets/javascripts/notes.js
+++ b/app/assets/javascripts/notes.js
@@ -46,6 +46,26 @@ var NoteList = {
".js-note-delete",
NoteList.removeNote);
+ // show the edit note form
+ $(document).on("click",
+ ".js-note-edit",
+ NoteList.showEditNoteForm);
+
+ // cancel note editing
+ $(document).on("click",
+ ".note-edit-cancel",
+ NoteList.cancelNoteEdit);
+
+ // delete note attachment
+ $(document).on("click",
+ ".js-note-attachment-delete",
+ NoteList.deleteNoteAttachment);
+
+ // update the note after editing
+ $(document).on("ajax:complete",
+ "form.edit_note",
+ NoteList.updateNote);
+
// reset main target form after submit
$(document).on("ajax:complete",
".js-main-target-form",
@@ -53,12 +73,12 @@ var NoteList = {
$(document).on("click",
- ".js-choose-note-attachment-button",
- NoteList.chooseNoteAttachment);
+ ".js-choose-note-attachment-button",
+ NoteList.chooseNoteAttachment);
$(document).on("click",
- ".js-show-outdated-discussion",
- function(e) { $(this).next('.outdated-discussion').show(); e.preventDefault() });
+ ".js-show-outdated-discussion",
+ function(e) { $(this).next('.outdated-discussion').show(); e.preventDefault() });
},
@@ -97,8 +117,8 @@ var NoteList = {
/**
* Called when clicking the "Choose File" button.
- *
- * Opesn the file selection dialog.
+ *
+ * Opens the file selection dialog.
*/
chooseNoteAttachment: function() {
var form = $(this).closest("form");
@@ -133,7 +153,7 @@ var NoteList = {
/**
* Called in response to "cancel" on a diff note form.
- *
+ *
* Shows the reply button again.
* Removes the form and if necessary it's temporary row.
*/
@@ -177,6 +197,59 @@ var NoteList = {
},
/**
+ * Called in response to clicking the edit note link
+ *
+ * Replaces the note text with the note edit form
+ * Adds a hidden div with the original content of the note to fill the edit note form with
+ * if the user cancels
+ */
+ showEditNoteForm: function(e) {
+ e.preventDefault();
+ var note = $(this).closest(".note");
+ note.find(".note-text").hide();
+
+ // Show the attachment delete link
+ note.find(".js-note-attachment-delete").show();
+
+ var form = note.find(".note-edit-form");
+ form.show();
+
+
+ var textarea = form.find("textarea");
+ var p = $("<p></p>").text(textarea.val());
+ var hidden_div = $('<div class="note-original-content"></div>').append(p);
+ form.append(hidden_div);
+ hidden_div.hide();
+ textarea.focus();
+ },
+
+ /**
+ * Called in response to clicking the cancel button when editing a note
+ *
+ * Resets and hides the note editing form
+ */
+ cancelNoteEdit: function(e) {
+ e.preventDefault();
+ var note = $(this).closest(".note");
+ NoteList.resetNoteEditing(note);
+ },
+
+
+ /**
+ * Called in response to clicking the delete attachment link
+ *
+ * Removes the attachment wrapper view, including image tag if it exists
+ * Resets the note editing form
+ */
+ deleteNoteAttachment: function() {
+ var note = $(this).closest(".note");
+ note.find(".note-attachment").remove();
+ NoteList.resetNoteEditing(note);
+ NoteList.rewriteTimestamp(note.find(".note-last-update"));
+ },
+
+
+ /**
* Called when clicking on the "reply" button for a diff line.
*
* Shows the note form below the notes.
@@ -426,5 +499,65 @@ var NoteList = {
votes.find(".upvotes").text(votes.find(".upvotes").text().replace(/\d+/, upvotes));
votes.find(".downvotes").text(votes.find(".downvotes").text().replace(/\d+/, downvotes));
}
+ },
+
+ /**
+ * Called in response to the edit note form being submitted
+ *
+ * Updates the current note field.
+ * Hides the edit note form
+ */
+ updateNote: function(e, xhr, settings) {
+ response = JSON.parse(xhr.responseText);
+ if (response.success) {
+ var note_li = $("#note_" + response.id);
+ var note_text = note_li.find(".note-text");
+ note_text.html(response.note).show();
+
+ var note_form = note_li.find(".note-edit-form");
+ note_form.hide();
+ note_form.find(".btn-save").enableButton();
+
+ // Update the "Edited at xxx label" on the note to show it's just been updated
+ NoteList.rewriteTimestamp(note_li.find(".note-last-update"));
+ }
+ },
+
+ /**
+ * Called in response to the 'cancel note' link clicked, or after deleting a note attachment
+ *
+ * Hides the edit note form and shows the note
+ * Resets the edit note form textarea with the original content of the note
+ */
+ resetNoteEditing: function(note) {
+ note.find(".note-text").show();
+
+ // Hide the attachment delete link
+ note.find(".js-note-attachment-delete").hide();
+
+ // Put the original content of the note back into the edit form textarea
+ var form = note.find(".note-edit-form");
+ var original_content = form.find(".note-original-content");
+ form.find("textarea").val(original_content.text());
+ original_content.remove();
+
+ note.find(".note-edit-form").hide();
+ },
+
+ /**
+ * Utility function to generate new timestamp text for a note
+ *
+ */
+ rewriteTimestamp: function(element) {
+ // Strip all newlines from the existing timestamp
+ var ts = element.text().replace(/\n/g, ' ').trim();
+
+ // If the timestamp already has '(Edited xxx ago)' text, remove it
+ ts = ts.replace(new RegExp("\\(Edited [A-Za-z0-9 ]+\\)$", "gi"), "");
+
+ // Append "(Edited just now)"
+ ts = (ts + " <small>(Edited just now)</small>");
+
+ element.html(ts);
}
};
diff --git a/app/assets/stylesheets/gitlab_bootstrap/avatar.scss b/app/assets/stylesheets/gitlab_bootstrap/avatar.scss
index ed6ec77b89b..0b147faf59e 100644
--- a/app/assets/stylesheets/gitlab_bootstrap/avatar.scss
+++ b/app/assets/stylesheets/gitlab_bootstrap/avatar.scss
@@ -15,6 +15,7 @@
&.s16 { width: 16px; height: 16px; margin-right: 6px; }
&.s24 { width: 24px; height: 24px; margin-right: 8px; }
+ &.s26 { width: 26px; height: 26px; margin-right: 8px; }
&.s32 { width: 32px; height: 32px; margin-right: 10px; }
&.s90 { width: 90px; height: 90px; margin-right: 15px; }
}
diff --git a/app/assets/stylesheets/gitlab_bootstrap/nav.scss b/app/assets/stylesheets/gitlab_bootstrap/nav.scss
index 0fc8b21de7b..847c7180ce2 100644
--- a/app/assets/stylesheets/gitlab_bootstrap/nav.scss
+++ b/app/assets/stylesheets/gitlab_bootstrap/nav.scss
@@ -57,9 +57,21 @@
border-color: #CCC;
border-bottom: 1px solid #fff;
color: #333;
+ font-weight: bold;
}
}
}
&.nav-small-tabs > li > a { padding: 6px 9px; }
}
+
+
+
+/**
+ * fix to keep tooltips position in top navigation bar
+ *
+ */
+.navbar .nav > li {
+ position: relative;
+ white-space: nowrap;
+}
diff --git a/app/assets/stylesheets/sections/events.scss b/app/assets/stylesheets/sections/events.scss
index e8680dde507..d057bcf669c 100644
--- a/app/assets/stylesheets/sections/events.scss
+++ b/app/assets/stylesheets/sections/events.scss
@@ -58,6 +58,7 @@
background: #f9f9f9;
border-radius: 0;
color: #555;
+ margin: 0 20px;
}
.note-file-attach {
diff --git a/app/assets/stylesheets/sections/header.scss b/app/assets/stylesheets/sections/header.scss
index e315b4ebcaa..38c08814224 100644
--- a/app/assets/stylesheets/sections/header.scss
+++ b/app/assets/stylesheets/sections/header.scss
@@ -77,6 +77,7 @@ header {
top: -4px;
img {
width: 26px;
+ height: 26px;
@include border-radius(4px);
}
}
diff --git a/app/assets/stylesheets/sections/issues.scss b/app/assets/stylesheets/sections/issues.scss
index 5a1b476fe25..ed7902fec3a 100644
--- a/app/assets/stylesheets/sections/issues.scss
+++ b/app/assets/stylesheets/sections/issues.scss
@@ -106,3 +106,7 @@ input.check_all_issues {
#update_status {
width: 100px;
}
+
+.participants {
+ margin-bottom: 10px;
+}
diff --git a/app/assets/stylesheets/sections/notes.scss b/app/assets/stylesheets/sections/notes.scss
index 9fe7a24b461..bae1ac3aa9a 100644
--- a/app/assets/stylesheets/sections/notes.scss
+++ b/app/assets/stylesheets/sections/notes.scss
@@ -92,6 +92,10 @@ ul.notes {
.note-body {
@include md-typography;
margin-left: 45px;
+
+ .highlight {
+ @include border-radius(4px);
+ }
}
.note-header {
padding-bottom: 5px;
@@ -321,3 +325,32 @@ ul.notes {
float: left;
}
}
+
+.note-edit-form {
+ display: none;
+
+ .note_text {
+ border: 1px solid #DDD;
+ box-shadow: none;
+ font-size: 14px;
+ height: 80px;
+ width: 98.6%;
+ }
+
+ .form-actions {
+ padding-left: 20px;
+
+ .btn-save {
+ float: left;
+ }
+
+ .note-form-option {
+ float: left;
+ padding: 2px 0 0 25px;
+ }
+ }
+}
+
+.js-note-attachment-delete {
+ display: none;
+}
diff --git a/app/contexts/issues/list_context.rb b/app/contexts/issues/list_context.rb
index a35bddd6443..906a83b58a6 100644
--- a/app/contexts/issues/list_context.rb
+++ b/app/contexts/issues/list_context.rb
@@ -8,7 +8,7 @@ module Issues
@issues = case params[:status]
when issues_filter[:all] then @project.issues
when issues_filter[:closed] then @project.issues.closed
- when issues_filter[:to_me] then @project.issues.assigned(current_user)
+ when issues_filter[:to_me] then @project.issues.assigned_to(current_user)
when issues_filter[:by_me] then @project.issues.authored(current_user)
else @project.issues.opened
end
diff --git a/app/contexts/merge_requests_load_context.rb b/app/contexts/merge_requests_load_context.rb
index fde04085f26..fd44572c0eb 100644
--- a/app/contexts/merge_requests_load_context.rb
+++ b/app/contexts/merge_requests_load_context.rb
@@ -9,7 +9,7 @@ class MergeRequestsLoadContext < BaseContext
merge_requests = case type
when 'all' then merge_requests
when 'closed' then merge_requests.closed
- when 'assigned-to-me' then merge_requests.opened.assigned(current_user)
+ when 'assigned-to-me' then merge_requests.opened.assigned_to(current_user)
else merge_requests.opened
end
diff --git a/app/contexts/projects/create_context.rb b/app/contexts/projects/create_context.rb
index 2922564ba20..d3b8dee3948 100644
--- a/app/contexts/projects/create_context.rb
+++ b/app/contexts/projects/create_context.rb
@@ -51,6 +51,7 @@ module Projects
if shell.import_repository(@project.path_with_namespace, @project.import_url)
# We should create satellite for imported repo
@project.satellite.create unless @project.satellite.exists?
+ @project.imported = true
true
else
@project.errors.add(:import_url, 'cannot clone repo')
diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb
index c38461c89db..749c8fbecef 100644
--- a/app/controllers/admin/groups_controller.rb
+++ b/app/controllers/admin/groups_controller.rb
@@ -8,10 +8,6 @@ class Admin::GroupsController < Admin::ApplicationController
end
def show
- @projects = Project.scoped
- @projects = @projects.not_in_group(@group) if @group.projects.present?
- @projects = @projects.all
- @projects.reject!(&:empty_repo?)
end
def new
diff --git a/app/controllers/admin/teams/projects_controller.rb b/app/controllers/admin/teams/projects_controller.rb
index 8584a188b20..687f8f68bf2 100644
--- a/app/controllers/admin/teams/projects_controller.rb
+++ b/app/controllers/admin/teams/projects_controller.rb
@@ -12,7 +12,7 @@ class Admin::Teams::ProjectsController < Admin::Teams::ApplicationController
user_team.assign_to_projects(project_ids, access)
end
- redirect_to admin_team_path(user_team), notice: 'Team of users was successfully assgned to projects.'
+ redirect_to admin_team_path(user_team), notice: 'Team of users was successfully assigned to projects.'
end
def edit
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index 185ad181b2a..ec3209fdfe2 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -55,8 +55,14 @@ class Admin::UsersController < Admin::ApplicationController
def create
admin = params[:user].delete("admin")
- @admin_user = User.new(params[:user], as: :admin)
+ opts = {
+ force_random_password: true,
+ password_expires_at: Time.now
+ }
+
+ @admin_user = User.new(params[:user].merge(opts), as: :admin)
@admin_user.admin = (admin && admin.to_i > 0)
+ @admin_user.created_by_id = current_user.id
respond_to do |format|
if @admin_user.save
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 09af5b94164..fda05feefc0 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -1,6 +1,7 @@
class ApplicationController < ActionController::Base
before_filter :authenticate_user!
before_filter :reject_blocked!
+ before_filter :check_password_expiration
before_filter :set_current_user_for_thread
before_filter :add_abilities
before_filter :dev_tools if Rails.env == 'development'
@@ -156,4 +157,10 @@ class ApplicationController < ActionController::Base
gon.gravatar_url = request.ssl? || Gitlab.config.gitlab.https ? Gitlab.config.gravatar.ssl_url : Gitlab.config.gravatar.plain_url
gon.relative_url_root = Gitlab.config.gitlab.relative_url_root
end
+
+ def check_password_expiration
+ if current_user && current_user.password_expires_at && current_user.password_expires_at < Time.now
+ redirect_to new_profile_password_path and return
+ end
+ end
end
diff --git a/app/controllers/notes_controller.rb b/app/controllers/notes_controller.rb
index 15ca963f281..dbec660b761 100644
--- a/app/controllers/notes_controller.rb
+++ b/app/controllers/notes_controller.rb
@@ -38,6 +38,32 @@ class NotesController < ProjectResourceController
end
end
+ def update
+ @note = @project.notes.find(params[:id])
+ return access_denied! unless can?(current_user, :admin_note, @note)
+
+ @note.update_attributes(params[:note])
+
+ respond_to do |format|
+ format.js do
+ render js: { success: @note.valid?, id: @note.id, note: view_context.markdown(@note.note) }.to_json
+ end
+ format.html do
+ redirect_to :back
+ end
+ end
+ end
+
+ def delete_attachment
+ @note = @project.notes.find(params[:id])
+ @note.remove_attachment!
+ @note.update_attribute(:attachment, nil)
+
+ respond_to do |format|
+ format.js { render nothing: true }
+ end
+ end
+
def preview
render text: view_context.markdown(params[:note])
end
diff --git a/app/controllers/passwords_controller.rb b/app/controllers/passwords_controller.rb
new file mode 100644
index 00000000000..0e5b42178a7
--- /dev/null
+++ b/app/controllers/passwords_controller.rb
@@ -0,0 +1,38 @@
+class PasswordsController < ApplicationController
+ layout 'navless'
+
+ skip_before_filter :check_password_expiration
+
+ before_filter :set_user
+ before_filter :set_title
+
+ def new
+ end
+
+ def create
+ new_password = params[:user][:password]
+ new_password_confirmation = params[:user][:password_confirmation]
+
+ result = @user.update_attributes(
+ password: new_password,
+ password_confirmation: new_password_confirmation
+ )
+
+ if result
+ @user.update_attributes(password_expires_at: nil)
+ redirect_to root_path, notice: 'Password successfully changed'
+ else
+ render :new
+ end
+ end
+
+ private
+
+ def set_user
+ @user = current_user
+ end
+
+ def set_title
+ @title = "New password"
+ end
+end
diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb
index 686edd8af80..6fa635d0e36 100644
--- a/app/controllers/profiles_controller.rb
+++ b/app/controllers/profiles_controller.rb
@@ -17,7 +17,7 @@ class ProfilesController < ApplicationController
end
def update
- if @user.update_attributes(user_attributes)
+ if @user.update_attributes(params[:user])
flash[:notice] = "Profile was successfully updated"
else
flash[:alert] = "Failed to update profile"
@@ -69,19 +69,6 @@ class ProfilesController < ApplicationController
@user = current_user
end
- def user_attributes
- user_attributes = params[:user]
-
- # Sanitize user input because we dont have strict
- # validation for this fields
- %w(name skype linkedin twitter bio).each do |attr|
- value = user_attributes[attr]
- user_attributes[attr] = sanitize(strip_tags(value)) if value.present?
- end
-
- user_attributes
- end
-
def authorize_change_password!
return render_404 if @user.ldap_user?
end
diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb
index 49b740af046..b91f68aab5e 100644
--- a/app/controllers/snippets_controller.rb
+++ b/app/controllers/snippets_controller.rb
@@ -7,8 +7,12 @@ class SnippetsController < ApplicationController
# Allow destroy snippet
before_filter :authorize_admin_snippet!, only: [:destroy]
+ before_filter :set_title
+
respond_to :html
+ layout 'navless'
+
def index
@snippets = Snippet.public.fresh.non_expired.page(params[:page]).per(20)
end
@@ -98,4 +102,8 @@ class SnippetsController < ApplicationController
def authorize_admin_snippet!
return render_404 unless can?(current_user, :admin_personal_snippet, @snippet)
end
+
+ def set_title
+ @title = 'Snippets'
+ end
end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 663a414fff2..a73d574f22e 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -142,7 +142,7 @@ module ApplicationHelper
end
def user_color_scheme_class
- COLOR_SCHEMES[current_user.try(:color_scheme_id)]
+ COLOR_SCHEMES[current_user.try(:color_scheme_id)] if defined?(current_user)
end
# Define whenever show last push event
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
index dc5aa6e1fb6..d7d50db8a2f 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -15,7 +15,7 @@ module IssuesHelper
# to allow filtering issues by an unassigned User or Milestone
def unassigned_filter
# Milestone uses :title, Issue uses :name
- OpenStruct.new(id: 0, title: 'Unspecified', name: 'Unassigned')
+ OpenStruct.new(id: 0, title: 'None (backlog)', name: 'Unassigned')
end
def issues_filter
diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb
index fbd0f01e5d4..a3ec4cca59d 100644
--- a/app/helpers/notes_helper.rb
+++ b/app/helpers/notes_helper.rb
@@ -28,4 +28,11 @@ module NotesHelper
def loading_new_notes?
params[:loading_new].present?
end
+
+ def note_timestamp(note)
+ # Shows the created at time and the updated at time if different
+ ts = "#{time_ago_in_words(note.created_at)} ago"
+ ts << content_tag(:small, " (Edited #{time_ago_in_words(note.updated_at)} ago)") if note.updated_at != note.created_at
+ ts.html_safe
+ end
end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 9b142714980..22aec62e2bf 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -17,7 +17,7 @@ module ProjectsHelper
end
def link_to_member(project, author, opts = {})
- default_opts = { avatar: true }
+ default_opts = { avatar: true, name: true, size: 16 }
opts = default_opts.merge(opts)
return "(deleted)" unless author
@@ -25,10 +25,10 @@ module ProjectsHelper
author_html = ""
# Build avatar image tag
- author_html << image_tag(gravatar_icon(author.try(:email)), width: 16, class: "avatar avatar-inline s16") if opts[:avatar]
+ author_html << image_tag(gravatar_icon(author.try(:email), opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt:'') if opts[:avatar]
# Build name span tag
- author_html << content_tag(:span, sanitize(author.name), class: 'author')
+ author_html << content_tag(:span, sanitize(author.name), class: 'author') if opts[:name]
author_html = author_html.html_safe
@@ -48,4 +48,36 @@ module ProjectsHelper
def remove_project_message(project)
"You are going to remove #{project.name_with_namespace}.\n Removed project CANNOT be restored!\n Are you ABSOLUTELY sure?"
end
+
+ def project_nav_tabs
+ @nav_tabs ||= get_project_nav_tabs(@project, current_user)
+ end
+
+ def project_nav_tab?(name)
+ project_nav_tabs.include? name
+ end
+
+ private
+
+ def get_project_nav_tabs(project, current_user)
+ nav_tabs = [:home]
+
+ if !project.empty_repo? && can?(current_user, :download_code, project)
+ nav_tabs << [:files, :commits, :network, :graphs]
+ end
+
+ if project.repo_exists? && project.merge_requests_enabled
+ nav_tabs << :merge_requests
+ end
+
+ if can?(current_user, :admin_project, project)
+ nav_tabs << :settings
+ end
+
+ [:issues, :wiki, :wall, :snippets].each do |feature|
+ nav_tabs << feature if project.send :"#{feature}_enabled"
+ end
+
+ nav_tabs.flatten
+ end
end
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 85337583640..38440859064 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -6,6 +6,7 @@
#
module Issuable
extend ActiveSupport::Concern
+ include Mentionable
included do
belongs_to :project
@@ -22,8 +23,10 @@ module Issuable
scope :closed, -> { with_state(:closed) }
scope :of_group, ->(group) { where(project_id: group.project_ids) }
scope :of_user_team, ->(team) { where(project_id: team.project_ids, assignee_id: team.member_ids) }
- scope :assigned, ->(u) { where(assignee_id: u.id)}
+ scope :assigned_to, ->(u) { where(assignee_id: u.id)}
scope :recent, -> { order("created_at DESC") }
+ scope :assigned, -> { where("assignee_id IS NOT NULL") }
+ scope :unassigned, -> { where("assignee_id IS NULL") }
delegate :name,
:email,
@@ -95,4 +98,18 @@ module Issuable
def votes_count
upvotes + downvotes
end
+
+ # Return all users participating on the discussion
+ def participants
+ users = []
+ users << author
+ users << assignee if is_assigned?
+ mentions = []
+ mentions << self.mentioned_users
+ notes.each do |note|
+ users << note.author
+ mentions << note.mentioned_users
+ end
+ users.concat(mentions.reduce([], :|)).uniq
+ end
end
diff --git a/app/models/concerns/mentionable.rb b/app/models/concerns/mentionable.rb
new file mode 100644
index 00000000000..f22070f8504
--- /dev/null
+++ b/app/models/concerns/mentionable.rb
@@ -0,0 +1,37 @@
+# == Mentionable concern
+#
+# Contains common functionality shared between Issues and Notes
+#
+# Used by Issue, Note
+#
+module Mentionable
+ extend ActiveSupport::Concern
+
+ def mentioned_users
+ users = []
+ return users if mentionable_text.blank?
+ has_project = self.respond_to? :project
+ matches = mentionable_text.scan(/@[a-zA-Z][a-zA-Z0-9_\-\.]*/)
+ matches.each do |match|
+ identifier = match.delete "@"
+ if has_project
+ id = project.users_projects.joins(:user).where(users: { username: identifier }).pluck(:user_id).first
+ else
+ id = User.where(username: identifier).pluck(:id).first
+ end
+ users << User.find(id) unless id.blank?
+ end
+ users.uniq
+ end
+
+ def mentionable_text
+ if self.class == Issue
+ description
+ elsif self.class == Note
+ note
+ else
+ nil
+ end
+ end
+
+end
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 91dd6477b04..de6e015c68e 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -27,7 +27,7 @@ class Issue < ActiveRecord::Base
scope :cared, ->(user) { where(assignee_id: user) }
scope :authored, ->(user) { where(author_id: user) }
- scope :open_for, ->(user) { opened.assigned(user) }
+ scope :open_for, ->(user) { opened.assigned_to(user) }
state_machine :state, initial: :opened do
event :close do
diff --git a/app/models/key.rb b/app/models/key.rb
index 185aef46e9e..61b4838f884 100644
--- a/app/models/key.rb
+++ b/app/models/key.rb
@@ -22,7 +22,7 @@ class Key < ActiveRecord::Base
before_validation :strip_white_space
validates :title, presence: true, length: { within: 0..255 }
- validates :key, presence: true, length: { within: 0..5000 }, format: { with: /\Assh-.*\Z/ }, uniqueness: true
+ validates :key, presence: true, length: { within: 0..5000 }, format: { with: /\A(ssh|ecdsa)-.*\Z/ }, uniqueness: true
validate :fingerprintable_key
delegate :name, :email, to: :user, prefix: true
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index b2ad1b76f1f..f41473336bc 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -91,6 +91,15 @@ class MergeRequest < ActiveRecord::Base
if target_branch == source_branch
errors.add :branch_conflict, "You can not use same branch for source and target branches"
end
+
+ if opened? || reopened?
+ similar_mrs = self.project.merge_requests.where(source_branch: source_branch, target_branch: target_branch).opened
+ similar_mrs = similar_mrs.where('id not in (?)', self.id) if self.id
+
+ if similar_mrs.any?
+ errors.add :base, "There is already an open merge request for this branches"
+ end
+ end
end
def reload_code
diff --git a/app/models/note.rb b/app/models/note.rb
index 9a3481faaaa..56a8749e47d 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -19,6 +19,8 @@ require 'carrierwave/orm/activerecord'
require 'file_size_validator'
class Note < ActiveRecord::Base
+ include Mentionable
+
attr_accessible :note, :noteable, :noteable_id, :noteable_type, :project_id,
:attachment, :line_code, :commit_id
diff --git a/app/models/project.rb b/app/models/project.rb
index 22a9c1ffd63..e3f88b2cf98 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -90,7 +90,7 @@ class Project < ActiveRecord::Base
validates_uniqueness_of :path, scope: :namespace_id
validates :import_url,
- format: { with: URI::regexp(%w(http https)), message: "should be a valid url" },
+ format: { with: URI::regexp(%w(git http https)), message: "should be a valid url" },
if: :import?
validate :check_limit
@@ -413,6 +413,10 @@ class Project < ActiveRecord::Base
!(forked_project_link.nil? || forked_project_link.forked_from_project.nil?)
end
+ def imported?
+ imported
+ end
+
def rename_repo
old_path_with_namespace = File.join(namespace_dir, path_was)
new_path_with_namespace = File.join(namespace_dir, path)
diff --git a/app/models/user.rb b/app/models/user.rb
index 0a3a40b994c..ddbdec8acfc 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -42,8 +42,11 @@ class User < ActiveRecord::Base
attr_accessible :email, :password, :password_confirmation, :remember_me, :bio, :name, :username,
:skype, :linkedin, :twitter, :color_scheme_id, :theme_id, :force_random_password,
- :extern_uid, :provider, as: [:default, :admin]
- attr_accessible :projects_limit, :can_create_team, :can_create_group, as: :admin
+ :extern_uid, :provider, :password_expires_at,
+ as: [:default, :admin]
+
+ attr_accessible :projects_limit, :can_create_team, :can_create_group,
+ as: :admin
attr_accessor :force_random_password
@@ -113,7 +116,10 @@ class User < ActiveRecord::Base
validate :namespace_uniq, if: ->(user) { user.username_changed? }
before_validation :generate_password, on: :create
+ before_validation :sanitize_attrs
+
before_save :ensure_authentication_token
+
alias_attribute :private_token, :authentication_token
delegate :path, to: :namespace, allow_nil: true, prefix: true
@@ -364,4 +370,15 @@ class User < ActiveRecord::Base
def accessible_deploy_keys
DeployKey.in_projects(self.master_projects).uniq
end
+
+ def created_by
+ User.find_by_id(created_by_id) if created_by_id
+ end
+
+ def sanitize_attrs
+ %w(name username skype linkedin twitter bio).each do |attr|
+ value = self.send(attr)
+ self.send("#{attr}=", Sanitize.clean(value)) if value.present?
+ end
+ end
end
diff --git a/app/observers/project_observer.rb b/app/observers/project_observer.rb
index bd88bb838ef..3d4d161a1a2 100644
--- a/app/observers/project_observer.rb
+++ b/app/observers/project_observer.rb
@@ -1,13 +1,13 @@
class ProjectObserver < BaseObserver
def after_create(project)
- unless project.forked?
- GitlabShellWorker.perform_async(
- :add_repository,
- project.path_with_namespace
- )
-
- log_info("#{project.owner.name} created a new project \"#{project.name_with_namespace}\"")
- end
+ return true if project.forked? || project.imported?
+
+ GitlabShellWorker.perform_async(
+ :add_repository,
+ project.path_with_namespace
+ )
+
+ log_info("#{project.owner.name} created a new project \"#{project.name_with_namespace}\"")
end
def after_update(project)
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index 379d2c54629..b0243481b35 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -110,9 +110,11 @@ class NotificationService
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)
+ if target.respond_to?(:participants)
+ recipients = target.participants
+ else
+ recipients = []
+ end
end
# Get users who left comment in thread
@@ -181,7 +183,12 @@ class NotificationService
end
def new_resource_email(target, method)
- recipients = reject_muted_users([target.assignee], target.project)
+ if target.respond_to?(:participants)
+ recipients = target.participants
+ else
+ recipients = []
+ end
+ recipients = reject_muted_users(recipients, target.project)
recipients = recipients.concat(project_watchers(target.project)).uniq
recipients.delete(target.author)
diff --git a/app/views/admin/hooks/_data_ex.html.erb b/app/views/admin/hooks/_data_ex.html.erb
index eeb78b5f0c5..b69aa92716d 100644
--- a/app/views/admin/hooks/_data_ex.html.erb
+++ b/app/views/admin/hooks/_data_ex.html.erb
@@ -1,23 +1,26 @@
<% data_ex_str = <<eos
1. Project created:
{
- "created_at": "2012-07-21T07:30:54Z",
- "event_name": "project_create",
- "name": "StoreCloud",
- "owner_email": "johnsmith@gmail.com",
- "owner_name": "John Smith",
- "path": "storecloud",
- "project_id": 74
+ "created_at": "2012-07-21T07:30:54Z",
+ "event_name": "project_create",
+ "name": "StoreCloud",
+ "owner_email": "johnsmith@gmail.com"
+ "owner_name": "John Smit",
+ "path": "stormcloud",
+ "path_with_namespace": "jsmith/stormcloud",
+ "project_id": 74,
}
2. Project destroyed:
{
- "event_name": "project_destroy",
- "name": "Underscore",
- "owner_email": "johnsmith@gmail.com",
- "owner_name": "John Smith",
- "path": "underscore",
- "project_id": 73
+ "created_at": "2012-07-21T07:30:58Z",
+ "event_name": "project_destroy",
+ "name": "Underscore",
+ "owner_email": "johnsmith@gmail.com"
+ "owner_name": "John Smith",
+ "path": "underscore",
+ "path_with_namespace": "jsmith/underscore",
+ "project_id": 73,
}
3. New Team Member:
diff --git a/app/views/admin/teams/members/new.html.haml b/app/views/admin/teams/members/new.html.haml
index d3929cb7cdf..96b6d61b974 100644
--- a/app/views/admin/teams/members/new.html.haml
+++ b/app/views/admin/teams/members/new.html.haml
@@ -1,29 +1,27 @@
%h3.page_title
- Team: #{@team.name}
-
-%fieldset
- %legend Members (#{@team.members.count})
- = form_tag admin_team_members_path(@team), id: "team_members", class: "bulk_import", method: :post do
- %table#members_list
- %thead
- %tr
- %th User name
- %th Default project access
- %th Team access
- %th
- - @team.members.each do |member|
- %tr.member
- %td
- = link_to [:admin, member] do
- = member.name
- %small= "(#{member.email})"
- %td= @team.human_default_projects_access(member)
- %td= @team.admin?(member) ? "Admin" : "Member"
- %td
- %tr
- %td= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name_with_username), multiple: true, data: {placeholder: 'Select users'}, class: 'chosen span5'
- %td= select_tag :default_project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3" }
- %td
- %span= check_box_tag :group_admin
- %span Admin?
- %td= submit_tag 'Add', class: "btn btn-primary", id: :add_members_to_team
+ New members for
+ = link_to @team.name, admin_team_path(@team)
+ team
+%hr
+= form_tag admin_team_members_path(@team), id: "team_members", class: "bulk_import", method: :post do
+ - if @team.errors.any?
+ .alert.alert-error
+ %span= @team.errors.full_messages.first
+ .clearfix
+ = label_tag :user_ids do
+ Users to add
+ .input
+ = select_tag :user_ids, options_from_collection_for_select(@users , :id, :name_with_username), multiple: true, data: {placeholder: 'Select users'}, class: 'chosen span5'
+ .clearfix.group-description-holder
+ = label_tag :default_project_access do
+ Default permission in projects
+ .input
+ = select_tag :default_project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3" }
+ .clearfix
+ = label_tag :group_admin do
+ Is team admin
+ .input
+ = check_box_tag :group_admin
+ .clearfix.form-actions
+ = submit_tag 'Add users into team', class: "btn btn-primary", id: :add_members_to_team
+ = link_to 'Cancel', :back, class: "btn"
diff --git a/app/views/admin/teams/projects/new.html.haml b/app/views/admin/teams/projects/new.html.haml
index dcb3dbbc433..21bf65f9c3d 100644
--- a/app/views/admin/teams/projects/new.html.haml
+++ b/app/views/admin/teams/projects/new.html.haml
@@ -8,7 +8,7 @@
.input
= select_tag :project_ids, options_from_collection_for_select(@projects , :id, :name_with_namespace), multiple: true, data: {placeholder: 'Select projects'}, class: 'chosen span5'
- %h6 Choose greatest user acces for your team in this projects:
+ %h6 Choose greatest user access for your team in these projects:
.clearfix
= label_tag :greatest_project_access, "Greatest Access"
.input
diff --git a/app/views/admin/users/_form.html.haml b/app/views/admin/users/_form.html.haml
index 9bde50f8947..fdf37965091 100644
--- a/app/views/admin/users/_form.html.haml
+++ b/app/views/admin/users/_form.html.haml
@@ -24,19 +24,25 @@
= f.text_field :email, required: true, autocomplete: "off"
%span.help-inline * required
- %fieldset
- %legend Password
- .clearfix
- = f.label :password
- .input= f.password_field :password, disabled: f.object.force_random_password
- .clearfix
- = f.label :password_confirmation
- .input= f.password_field :password_confirmation, disabled: f.object.force_random_password
- -if f.object.new_record?
+ - if @admin_user.new_record?
+ %fieldset
+ %legend Password
+ .clearfix
+ = f.label :password
+ .input
+ %strong
+ A temporary password will be generated and sent to user.
+ %br
+ User will be forced to change it after first sign in
+ - else
+ %fieldset
+ %legend Password
+ .clearfix
+ = f.label :password
+ .input= f.password_field :password, disabled: f.object.force_random_password
.clearfix
- = f.label :force_random_password do
- %span Generate random password
- .input= f.check_box :force_random_password, {}, true, nil
+ = f.label :password_confirmation
+ .input= f.password_field :password_confirmation, disabled: f.object.force_random_password
%fieldset
%legend Access
diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml
index 6709b8f8a6b..3edd76a7c1b 100644
--- a/app/views/admin/users/show.html.haml
+++ b/app/views/admin/users/show.html.haml
@@ -1,32 +1,67 @@
+%h3.page_title
+ User:
+ = @admin_user.name
+ - if @admin_user.blocked?
+ %span.cred (Blocked)
+ - if @admin_user.admin
+ %span.cred (Admin)
+
+ .pull-right
+ = link_to edit_admin_user_path(@admin_user), class: "btn grouped btn-small" do
+ %i.icon-edit
+ Edit
+ - unless @admin_user == current_user
+ - if @admin_user.blocked?
+ = link_to 'Unblock', unblock_admin_user_path(@admin_user), method: :put, class: "btn grouped btn-small success"
+ - else
+ = link_to 'Block', block_admin_user_path(@admin_user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn grouped btn-small btn-remove"
+ = link_to 'Destroy', [:admin, @admin_user], confirm: "USER #{@admin_user.name} WILL BE REMOVED! Are you sure?", method: :delete, class: "btn grouped btn-small btn-remove"
+%hr
+
.row
.span6
- %h3.page_title
- = image_tag gravatar_icon(@admin_user.email, 90), class: "avatar s90"
- = @admin_user.name
- - if @admin_user.blocked?
- %span.cred (Blocked)
- - if @admin_user.admin
- %span.cred (Admin)
- .pull-right
- = link_to edit_admin_user_path(@admin_user), class: "btn pull-right" do
- %i.icon-edit
- Edit
- %br
- %small @#{@admin_user.username}
- %br
- %small member since #{@admin_user.created_at.stamp("Nov 12, 2031")}
- .clearfix
- %hr
- %p
- %span.btn.btn-small
- %i.icon-envelope
- = mail_to @admin_user.email
- - unless @admin_user == current_user
- - if @admin_user.blocked?
- = link_to 'Unblock', unblock_admin_user_path(@admin_user), method: :put, class: "btn btn-small success"
- - else
- = link_to 'Block', block_admin_user_path(@admin_user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn btn-small btn-remove"
- = link_to 'Destroy', [:admin, @admin_user], confirm: "USER #{@admin_user.name} WILL BE REMOVED! Are you sure?", method: :delete, class: "btn btn-small btn-remove"
+ .ui-box
+ %h5.title
+ Account:
+ .pull-right
+ = image_tag gravatar_icon(@admin_user.email, 32), class:"avatar s32", alt:""
+ %ul.well-list
+ %li
+ %span.light Name:
+ %strong= @admin_user.name
+ %li
+ %span.light Username:
+ %strong
+ = @admin_user.username
+ %li
+ %span.light Email:
+ %strong
+ = mail_to @admin_user.email
+
+ %li
+ %span.light Member since:
+ %strong
+ = @admin_user.created_at.stamp("Nov 12, 2031")
+
+ %li
+ %span.light Last sign-in at:
+ %strong
+ - if @admin_user.last_sign_in_at
+ = @admin_user.last_sign_in_at.stamp("Nov 12, 2031")
+ - else
+ never
+
+ - if @admin_user.ldap_user?
+ %li
+ %span.light LDAP uid:
+ %strong
+ = @admin_user.extern_uid
+
+ - if @admin_user.created_by
+ %li
+ %span.light Created by:
+ %strong
+ = link_to @admin_user.created_by.name, [:admin, @admin_user.created_by]
%hr
%h5
Add User to Projects
@@ -67,11 +102,11 @@
.span6
- = render 'users/profile', user: @admin_user
.ui-box
%h5.title Projects (#{@projects.count})
%ul.well-list
- @projects.sort_by(&:name_with_namespace).each do |project|
+ - tm = project.team.get_tm(@admin_user.id)
%li
= link_to admin_project_path(project), class: dom_class(project) do
- if project.namespace
@@ -79,16 +114,17 @@
\/
%strong.well-title
= truncate(project.name, length: 45)
- %span.pull-right.light
- - if project.owner == @admin_user
- %i.icon-wrench
- - tm = project.team.get_tm(@admin_user.id)
- - if tm
- = tm.project_access_human
- = link_to edit_admin_project_member_path(project, tm.user), class: "btn btn-small" do
+
+ - if project.owner == @admin_user
+ %span.label.label-info owner
+
+ - if tm
+ .pull-right
+ = link_to edit_admin_project_member_path(project, tm.user), class: "btn grouped btn-small" do
%i.icon-edit
- = link_to admin_project_member_path(project, tm.user), confirm: remove_from_project_team_message(project, @admin_user), method: :delete, class: "btn btn-small btn-remove" do
+ = link_to admin_project_member_path(project, tm.user), confirm: remove_from_project_team_message(project, @admin_user), method: :delete, class: "btn grouped btn-small btn-remove" do
%i.icon-remove
- %p.light
- %i.icon-wrench
- &ndash; user is a project owner
+
+ .pull-right.light
+ = tm.project_access_human
+ &nbsp;
diff --git a/app/views/dashboard/projects.html.haml b/app/views/dashboard/projects.html.haml
index 9b16db340b2..25239105e3b 100644
--- a/app/views/dashboard/projects.html.haml
+++ b/app/views/dashboard/projects.html.haml
@@ -20,22 +20,24 @@
= nav_tab :scope, 'joined' do
= link_to "Joined", projects_dashboard_path(scope: 'joined')
- %p.light Filter by label:
- %ul.bordered-list
- - @labels.each do |label|
- %li{ class: (label.name == params[:label]) ? 'active' : 'light' }
- = link_to projects_dashboard_path(scope: params[:scope], label: label.name) do
- %i.icon-tag
- = label.name
+ - if @labels.any?
+ %p.light Filter by label:
+ %ul.bordered-list
+ - @labels.each do |label|
+ %li{ class: (label.name == params[:label]) ? 'active' : 'light' }
+ = link_to projects_dashboard_path(scope: params[:scope], label: label.name) do
+ %i.icon-tag
+ = label.name
.span9
- = form_tag projects_dashboard_path, method: 'get' do
- %fieldset.dashboard-search-filter
- = hidden_field_tag "scope", params[:scope]
- = search_field_tag "search", params[:search], { id: 'dashboard_projects_search', placeholder: 'Search', class: 'left input-xxlarge'}
- = button_tag type: 'submit', class: 'btn' do
- %i.icon-search
+ - if @projects.any?
+ = form_tag projects_dashboard_path, method: 'get' do
+ %fieldset.dashboard-search-filter
+ = hidden_field_tag "scope", params[:scope]
+ = search_field_tag "search", params[:search], { id: 'dashboard_projects_search', placeholder: 'Search', class: 'left input-xxlarge'}
+ = button_tag type: 'submit', class: 'btn' do
+ %i.icon-search
%ul.bordered-list
- @projects.each do |project|
diff --git a/app/views/devise/sessions/_oauth_providers.html.haml b/app/views/devise/sessions/_oauth_providers.html.haml
index 710a5d52514..935bc6af505 100644
--- a/app/views/devise/sessions/_oauth_providers.html.haml
+++ b/app/views/devise/sessions/_oauth_providers.html.haml
@@ -1,8 +1,9 @@
-- if enabled_oauth_providers.present?
+- providers = (enabled_oauth_providers - [:ldap])
+- if providers.present?
%hr
%div{:'data-no-turbolink' => 'data-no-turbolink'}
%span Sign in with: &nbsp;
- - (enabled_oauth_providers - [:ldap]).each do |provider|
+ - providers.each do |provider|
%span
- if default_providers.include?(provider)
= link_to authbutton(provider, 32), omniauth_authorize_path(resource_name, provider)
diff --git a/app/views/edit_tree/show.html.haml b/app/views/edit_tree/show.html.haml
index 17d813ce75e..101b479afed 100644
--- a/app/views/edit_tree/show.html.haml
+++ b/app/views/edit_tree/show.html.haml
@@ -23,7 +23,7 @@
= hidden_field_tag 'last_commit', @last_commit
= hidden_field_tag 'content', '', id: :file_content
.commit-button-annotation
- = button_tag "Commit", class: 'btn commit-btn js-commit-button'
+ = button_tag "Commit changes", class: 'btn commit-btn js-commit-button btn-primary'
.message
to branch
%strong= @ref
diff --git a/app/views/events/_commit.html.haml b/app/views/events/_commit.html.haml
index 2d80fc103f6..b50faf5a25c 100644
--- a/app/views/events/_commit.html.haml
+++ b/app/views/events/_commit.html.haml
@@ -1,6 +1,6 @@
%li.commit
%p
- = link_to commit[:id][0..8], project_commit_path(project, commit[:id]), class: "commit_short_id"
+ = link_to commit[:id][0..8], project_commit_path(project, commit[:id]), class: "commit_short_id", alt: ''
%span= commit[:author][:name]
&ndash;
= image_tag gravatar_icon(commit[:author][:email]), class: "avatar", width: 16
diff --git a/app/views/events/_event.html.haml b/app/views/events/_event.html.haml
index e2bf54ea5c9..80d16631fa0 100644
--- a/app/views/events/_event.html.haml
+++ b/app/views/events/_event.html.haml
@@ -4,7 +4,7 @@
#{time_ago_in_words(event.created_at)} ago.
= cache event do
- = image_tag gravatar_icon(event.author_email), class: "avatar s24"
+ = image_tag gravatar_icon(event.author_email), class: "avatar s24", alt:''
- if event.push?
= render "events/event/push", event: event
diff --git a/app/views/groups/people.html.haml b/app/views/groups/people.html.haml
index 3e4eb082f56..44f623006eb 100644
--- a/app/views/groups/people.html.haml
+++ b/app/views/groups/people.html.haml
@@ -12,7 +12,7 @@
%ul.well-list
- @users.each do |user|
%li
- = image_tag gravatar_icon(user.email, 16), class: "avatar s16"
+ = image_tag gravatar_icon(user.email, 16), class: "avatar s16" , alt: ''
%strong= user.name
%span.cgray= user.email
- if @group.owner == user
diff --git a/app/views/help/index.html.haml b/app/views/help/index.html.haml
index 1685c6eec53..80ddc05b503 100644
--- a/app/views/help/index.html.haml
+++ b/app/views/help/index.html.haml
@@ -26,7 +26,7 @@
on the top of this page
%li
Ask in our
- = link_to "support forum", "https://groups.google.com/forum/#!forum/gitlabhq"
+ = link_to "mailing list", "https://groups.google.com/forum/#!forum/gitlabhq"
or on
= link_to "Stack Overflow", "http://stackoverflow.com/questions/tagged/gitlab"
%li
diff --git a/app/views/help/markdown.html.haml b/app/views/help/markdown.html.haml
index 92c1e49be49..b4ff88c7f93 100644
--- a/app/views/help/markdown.html.haml
+++ b/app/views/help/markdown.html.haml
@@ -2,126 +2,6 @@
%h3.page_title GitLab Flavored Markdown
%br
- .row
- .span8
- %p
- For GitLab we developed something we call "GitLab Flavored Markdown" (GFM).
- It extends the standard Markdown in a few significant ways adds some useful functionality.
-
- %p You can use GFM in:
- %ul
- %li commit messages
- %li comments
- %li wall posts
- %li issues
- %li merge requests
- %li milestones
- %li wiki pages
-
- .span4
- .alert.alert-info
- %p
- If you're not already familiar with Markdown, you should spend 15 minutes and go over the excellent
- %strong= link_to "Markdown Syntax Guide", "http://daringfireball.net/projects/markdown/syntax"
- at Daring Fireball.
-
- .row
- .span8
- %h3 Differences from traditional Markdown
-
- %h4 Newlines
-
- %p
- The biggest difference that GFM introduces is in the handling of linebreaks.
- With traditional Markdown you can hard wrap paragraphs of text and they will be combined into a single paragraph. We find this to be the cause of a huge number of unintentional formatting errors.
- GFM treats newlines in paragraph-like content as real line breaks, which is probably what you intended.
-
-
- %p The next paragraph contains two phrases separated by a single newline character:
- %pre= "Roses are red\nViolets are blue"
- %p becomes
- = markdown "Roses are red\nViolets are blue"
-
- %h4 Multiple underscores in words
-
- %p
- It is not reasonable to italicize just <em>part</em> of a word, especially when you're dealing with code and names often appear with multiple underscores.
- Therefore, GFM ignores multiple underscores in words.
-
- %pre= "perform_complicated_task\ndo_this_and_do_that_and_another_thing"
- %p becomes
- = markdown "perform_complicated_task\ndo_this_and_do_that_and_another_thing"
-
- %h4 URL autolinking
-
- %p
- GFM will autolink standard URLs you copy and paste into your text.
- So if you want to link to a URL (instead of a textual link), you can simply put the URL in verbatim and it will be turned into a link to that URL.
-
- %h4 Fenced code blocks
-
- %p
- Markdown converts text with four spaces at the front of each line to code blocks.
- GFM supports that, but we also support fenced blocks.
- Just wrap your code blocks in <code>```</code> and you won't need to indent manually to trigger a code block.
-
- %pre= %Q{```ruby\nrequire 'redcarpet'\nmarkdown = Redcarpet.new("Hello World!")\nputs markdown.to_html\n```}
- %p becomes
- = markdown %Q{```ruby\nrequire 'redcarpet'\nmarkdown = Redcarpet.new("Hello World!")\nputs markdown.to_html\n```}
-
- %h4 Emoji
-
- .row
- .span8
- :ruby
- puts markdown %Q{Sometimes you want to be :cool: and add some :sparkles: to your :speech_balloon:. Well we have a :gift: for you:
-
- :exclamation: You can use emoji anywhere GFM is supported. :sunglasses:
-
- You can use it to point out a :bug: or warn about :monkey:patches. And if someone improves your really :snail: code, send them a :bouquet: or some :candy:. People will :heart: you for that.
-
- If you are :new: to this, don't be :fearful:. You can easily join the emoji :circus_tent:. All you need to do is to :book: up on the supported codes.
- }
-
- .span4
- .alert.alert-info
- %p
- Consult the
- %strong= link_to "Emoji Cheat Sheet", "http://www.emoji-cheat-sheet.com/"
- for a list of all supported emoji codes.
-
- .row
- .span8
- %h4 Special GitLab references
-
- %p
- GFM recognizes special references.
- You can easily reference e.g. a team member, an issue or a commit within a project.
- GFM will turn that reference into a link so you can navigate between them easily.
-
- %p GFM will recognize the following references:
- %ul
- %li
- %code @foo
- for team members
- %li
- %code #123
- for issues
- %li
- %code !123
- for merge request
- %li
- %code $123
- for snippets
- %li
- %code 1234567
- for commits
-
- -# this example will only be shown if the user has a project with at least one issue
- - if @project = current_user.authorized_projects.first
- - if issue = @project.issues.first
- %p For example in your #{link_to @project.name, project_path(@project)} project, writing:
- %pre= "This is related to ##{issue.id}. @#{current_user.username} is working on solving it."
- %p becomes:
- = markdown "This is related to ##{issue.id}. @#{current_user.username} is working on solving it."
- - @project = nil # Prevent this from bubbling up to page title
+ .help_body
+ = preserve do
+ = markdown File.read(Rails.root.join("doc", "markdown", "markdown.md"))
diff --git a/app/views/issues/_issues.html.haml b/app/views/issues/_issues.html.haml
index cc8d8d9cae2..fb15effceb8 100644
--- a/app/views/issues/_issues.html.haml
+++ b/app/views/issues/_issues.html.haml
@@ -53,7 +53,7 @@
- @project.users.sort_by(&:name).each do |user|
%li
= link_to project_issues_with_filter_path(@project, assignee_id: user.id) do
- = image_tag gravatar_icon(user.email), class: "avatar s16"
+ = image_tag gravatar_icon(user.email), class: "avatar s16", alt: ''
= user.name
.dropdown.inline.prepend-left-10
@@ -63,7 +63,7 @@
- if @milestone.present?
%strong= @milestone.title
- elsif params[:milestone_id] == "0"
- Unspecified
+ None (backlog)
- else
Any
%b.caret
@@ -72,7 +72,7 @@
= link_to project_issues_with_filter_path(@project, milestone_id: nil) do
Any
= link_to project_issues_with_filter_path(@project, milestone_id: 0) do
- Unspecified
+ None (backlog)
- issues_active_milestones.each do |milestone|
%li
= link_to project_issues_with_filter_path(@project, milestone_id: milestone.id) do
diff --git a/app/views/issues/show.html.haml b/app/views/issues/show.html.haml
index 2e204b8240d..891a01f14c9 100644
--- a/app/views/issues/show.html.haml
+++ b/app/views/issues/show.html.haml
@@ -65,4 +65,9 @@
- else
= link_to 'Close Issue', project_issue_path(@project, @issue, issue: {state_event: :close }, status_only: true), method: :put, class: "btn grouped close_issue", title: "Close Issue"
+.participants
+ %cite.cgray #{@issue.participants.count} participants
+ - @issue.participants.each do |participant|
+ = link_to_member(@project, participant, name: false, size: 24)
+
.voting_notes#notes= render "notes/notes_with_form"
diff --git a/app/views/layouts/_head_panel.html.haml b/app/views/layouts/_head_panel.html.haml
index f0b001f6efc..8a3581cd318 100644
--- a/app/views/layouts/_head_panel.html.haml
+++ b/app/views/layouts/_head_panel.html.haml
@@ -37,5 +37,4 @@
%i.icon-signout
%li
= link_to current_user, class: "profile-pic" do
- = image_tag gravatar_icon(current_user.email, 26)
-
+ = image_tag(gravatar_icon(current_user.email, 26), alt: '')
diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml
index 1c5781daab0..c01523c59cd 100644
--- a/app/views/layouts/nav/_project.html.haml
+++ b/app/views/layouts/nav/_project.html.haml
@@ -3,43 +3,48 @@
= link_to project_path(@project), title: "Project" do
%i.icon-home
- - unless @project.empty_repo?
- - if can? current_user, :download_code, @project
- = nav_link(controller: %w(tree blob blame)) do
- = link_to 'Files', project_tree_path(@project, @ref || @repository.root_ref)
- = nav_link(controller: %w(commit commits compare repositories protected_branches)) do
- = link_to "Commits", project_commits_path(@project, @ref || @repository.root_ref)
- = nav_link(controller: %w(network)) do
- = link_to "Network", project_network_path(@project, @ref || @repository.root_ref)
- = nav_link(controller: %w(graphs)) do
- = link_to "Graphs", project_graph_path(@project, @ref || @repository.root_ref)
-
- - if @project.issues_enabled
+ - if project_nav_tab? :files
+ = nav_link(controller: %w(tree blob blame)) do
+ = link_to 'Files', project_tree_path(@project, @ref || @repository.root_ref)
+
+ - if project_nav_tab? :commits
+ = nav_link(controller: %w(commit commits compare repositories protected_branches)) do
+ = link_to "Commits", project_commits_path(@project, @ref || @repository.root_ref)
+
+ - if project_nav_tab? :network
+ = nav_link(controller: %w(network)) do
+ = link_to "Network", project_network_path(@project, @ref || @repository.root_ref)
+
+ - if project_nav_tab? :graphs
+ = nav_link(controller: %w(graphs)) do
+ = link_to "Graphs", project_graph_path(@project, @ref || @repository.root_ref)
+
+ - if project_nav_tab? :issues
= nav_link(controller: %w(issues milestones labels)) do
= link_to url_for_project_issues do
Issues
- if @project.used_default_issues_tracker?
%span.count.issue_counter= @project.issues.opened.count
- - if @project.repo_exists? && @project.merge_requests_enabled
+ - if project_nav_tab? :merge_requests
= nav_link(controller: :merge_requests) do
= link_to project_merge_requests_path(@project) do
Merge Requests
%span.count.merge_counter= @project.merge_requests.opened.count
- - if @project.wiki_enabled
+ - if project_nav_tab? :wiki
= nav_link(controller: :wikis) do
= link_to 'Wiki', project_wiki_path(@project, :home)
- - if @project.wall_enabled
+ - if project_nav_tab? :wall
= nav_link(controller: :walls) do
= link_to 'Wall', project_wall_path(@project)
- - if @project.snippets_enabled
+ - if project_nav_tab? :snippets
= nav_link(controller: :snippets) do
= link_to 'Snippets', project_snippets_path(@project)
- - if can? current_user, :admin_project, @project
+ - if project_nav_tab? :settings
= nav_link(html_options: {class: "#{project_tab_class}"}) do
= link_to edit_project_path(@project), class: "stat-tab tab " do
Settings
diff --git a/app/views/layouts/nav/_team.html.haml b/app/views/layouts/nav/_team.html.haml
index 415e45104df..575c5b7e1f3 100644
--- a/app/views/layouts/nav/_team.html.haml
+++ b/app/views/layouts/nav/_team.html.haml
@@ -18,7 +18,7 @@
Members
%span.count= @team.members.count
- - if can? current_user, :admin_user_team, @team
+ - if can? current_user, :manage_user_team, @team
= nav_link(path: 'teams#edit') do
= link_to edit_team_path(@team), class: "stat-tab tab " do
Settings
diff --git a/app/views/layouts/snippets.html.haml b/app/views/layouts/snippets.html.haml
deleted file mode 100644
index e98aba445af..00000000000
--- a/app/views/layouts/snippets.html.haml
+++ /dev/null
@@ -1,20 +0,0 @@
-!!! 5
-%html{ lang: "en"}
- = render "layouts/head", title: "Snipepts"
- %body{class: "#{app_theme} application", :'data-page' => body_data_page}
- = render "layouts/head_panel", title: "Snippets"
- = render "layouts/flash"
- %nav.main-nav
- .container
- %ul
- = nav_link(path: 'snippets#user_index', html_options: {class: 'home'}) do
- = link_to user_snippets_path(current_user), title: "My Snippets" do
- %i.icon-home
- = nav_link(path: 'snippets#new') do
- = link_to new_snippet_path do
- New snippet
- = nav_link(path: 'snippets#index') do
- = link_to snippets_path do
- Discover snippets
- .container
- .content= yield
diff --git a/app/views/milestones/_issues.html.haml b/app/views/milestones/_issues.html.haml
new file mode 100644
index 00000000000..eccf3ddbfa5
--- /dev/null
+++ b/app/views/milestones/_issues.html.haml
@@ -0,0 +1,11 @@
+.ui-box
+ %h5.title= title
+ %ul.well-list
+ - issues.each do |issue|
+ %li
+ = link_to [@project, issue] do
+ %span.badge{class: issue.closed? ? 'badge-important' : 'badge-info'} ##{issue.id}
+ = link_to_gfm truncate(issue.title, length: 60), [@project, issue]
+ - if issue.assignee
+ .pull-right
+ = image_tag gravatar_icon(issue.assignee.email, 16), class: "avatar s16"
diff --git a/app/views/milestones/_merge_request.html.haml b/app/views/milestones/_merge_request.html.haml
new file mode 100644
index 00000000000..7f815894069
--- /dev/null
+++ b/app/views/milestones/_merge_request.html.haml
@@ -0,0 +1,5 @@
+%li
+ = link_to [@project, merge_request] do
+ %span.badge.badge-info ##{merge_request.id}
+ &ndash;
+ = link_to_gfm truncate(merge_request.title, length: 60), [@project, merge_request]
diff --git a/app/views/milestones/show.html.haml b/app/views/milestones/show.html.haml
index 034c37852f1..de33ab363e6 100644
--- a/app/views/milestones/show.html.haml
+++ b/app/views/milestones/show.html.haml
@@ -55,39 +55,52 @@
= markdown @milestone.description
-.row
- .span6
- .ui-box.milestone-issue-filter
- .title
- %ul.nav.nav-pills
- %li.active= link_to('Open Issues', '#')
- %li=link_to('All Issues', '#')
- %ul.well-list
- - @issues.each do |issue|
- %li{data: {closed: issue.closed?}}
- = link_to [@project, issue] do
- %span.badge.badge-info ##{issue.id}
- &ndash;
- = link_to_gfm truncate(issue.title, length: 60), [@project, issue]
+%ul.nav.nav-tabs
+ %li.active
+ = link_to '#tab-issues', 'data-toggle' => 'tab' do
+ Issues
+ %span.badge= @issues.count
+ %li
+ = link_to '#tab-merge-requests', 'data-toggle' => 'tab' do
+ Merge Requests
+ %span.badge= @merge_requests.count
+ %li
+ = link_to '#tab-participants', 'data-toggle' => 'tab' do
+ Participants
+ %span.badge= @users.count
- .span6
- .ui-box.milestone-merge-requests-filter
- .title
- %ul.nav.nav-pills
- %li.active= link_to('Open Merge Requests', '#')
- %li=link_to('All Merge Requests', '#')
- %ul.well-list
- - @merge_requests.each do |merge_request|
- %li{data: {closed: merge_request.closed? || merge_request.merged?}}
- = link_to [@project, merge_request] do
- %span.badge.badge-info ##{merge_request.id}
- &ndash;
- = link_to_gfm truncate(merge_request.title, length: 60), [@project, merge_request]
-%hr
-%h6 Participants:
-%div
- - @users.each do |user|
- = link_to_member(@project, user)
+.tab-content
+ .tab-pane.active#tab-issues
+ .row
+ .span4
+ = render('issues', title: 'Unstarted Issues (open and unassigned)', issues: @issues.opened.unassigned)
+ .span4
+ = render('issues', title: 'Ongoing Issues (open and assigned)', issues: @issues.opened.assigned)
+ .span4
+ = render('issues', title: 'Completed Issues (closed)', issues: @issues.closed)
+
+ .tab-pane#tab-merge-requests
+ .row
+ .span6
+ .ui-box
+ %h5.title Open
+ %ul.well-list
+ - @merge_requests.opened.each do |merge_request|
+ = render 'merge_request', merge_request: merge_request
+ .span6
+ .ui-box
+ %h5.title Closed
+ %ul.well-list
+ - @merge_requests.closed.each do |merge_request|
+ = render 'merge_request', merge_request: merge_request
-.clearfix
+ .tab-pane#tab-participants
+ %ul.bordered-list
+ - @users.each do |user|
+ %li
+ = link_to user, title: user.name, class: "dark" do
+ = image_tag gravatar_icon(user.email, 32), class: "avatar s32"
+ %strong= truncate(user.name, lenght: 40)
+ %br
+ %small.cgray= user.username
diff --git a/app/views/notes/_discussion.html.haml b/app/views/notes/_discussion.html.haml
index 24cb4228174..e65a4f390e6 100644
--- a/app/views/notes/_discussion.html.haml
+++ b/app/views/notes/_discussion.html.haml
@@ -8,7 +8,7 @@
= link_to "javascript:;", class: "js-details-target turn-off js-toggler-target" do
%i.icon-eye-open
Show discussion
- = image_tag gravatar_icon(note.author.email), class: "avatar s32"
+ = image_tag gravatar_icon(note.author.email), class: "avatar s32", alt: ''
%div
= link_to_member(@project, note.author, avatar: false)
- if note.for_merge_request?
diff --git a/app/views/notes/_note.html.haml b/app/views/notes/_note.html.haml
index b355e2a0bd4..6089b9505b2 100644
--- a/app/views/notes/_note.html.haml
+++ b/app/views/notes/_note.html.haml
@@ -6,13 +6,14 @@
Link here
&nbsp;
- if(note.author_id == current_user.id) || can?(current_user, :admin_note, @project)
- = link_to project_note_path(@project, note), title: "Remove comment", method: :delete, confirm: 'Are you sure you want to remove comment?', remote: true, class: "danger js-note-delete" do
+ = link_to "#", title: "Edit comment", class: "js-note-edit" do
+ %i.icon-edit
+ = link_to project_note_path(@project, note), title: "Remove comment", method: :delete, confirm: 'Are you sure you want to remove this comment?', remote: true, class: "danger js-note-delete" do
%i.icon-trash.cred
- = image_tag gravatar_icon(note.author.email), class: "avatar s32"
+ = image_tag gravatar_icon(note.author.email), class: "avatar s32", alt: ''
= link_to_member(@project, note.author, avatar: false)
%span.note-last-update
- = time_ago_in_words(note.updated_at)
- ago
+ = note_timestamp(note)
- if note.upvote?
%span.vote.upvote.label.label-success
@@ -25,13 +26,37 @@
.note-body
- = preserve do
- = markdown(note.note)
+ .note-text
+ = preserve do
+ = markdown(note.note)
+
+ .note-edit-form
+ = form_for note, url: project_note_path(@project, note), method: :put, remote: true do |f|
+ = f.text_area :note, class: 'note_text js-note-text js-gfm-input turn-on'
+
+ .form-actions
+ = f.submit 'Save changes', class: "btn btn-primary btn-save"
+
+ .note-form-option
+ %a.choose-btn.btn.btn-small.js-choose-note-attachment-button
+ %i.icon-paper-clip
+ %span Choose File ...
+ &nbsp;
+ %span.file_name.js-attachment-filename File name...
+ = f.file_field :attachment, class: "js-note-attachment-input hide"
+
+ = link_to 'Cancel', "#", class: "btn btn-cancel note-edit-cancel"
+
+
- if note.attachment.url
- - if note.attachment.image?
- = image_tag note.attachment.url, class: 'note-image-attach'
- .attachment.pull-right
- = link_to note.attachment.secure_url, target: "_blank" do
- %i.icon-paper-clip
- = note.attachment_identifier
- .clear
+ .note-attachment
+ - if note.attachment.image?
+ = image_tag note.attachment.url, class: 'note-image-attach'
+ .attachment.pull-right
+ = link_to note.attachment.secure_url, target: "_blank" do
+ %i.icon-paper-clip
+ = note.attachment_identifier
+ = link_to delete_attachment_project_note_path(@project, note),
+ title: "Delete this attachment", method: :delete, remote: true, confirm: 'Are you sure you want to remove the attachment?', class: "danger js-note-attachment-delete" do
+ %i.icon-trash.cred
+ .clear \ No newline at end of file
diff --git a/app/views/notify/new_user_email.html.haml b/app/views/notify/new_user_email.html.haml
index 9804fbdd51e..fc2c02ef827 100644
--- a/app/views/notify/new_user_email.html.haml
+++ b/app/views/notify/new_user_email.html.haml
@@ -8,13 +8,14 @@
%p
login..........................................
%code= @user['email']
-%p
- - unless Gitlab.config.gitlab.signup_enabled
+
+- if @user.created_by_id
+ %p
password..................................
%code= @password
-%p
- Please change your password immediately after login.
+ %p
+ You will be forced to change this password immediately after login.
%p
= link_to "Click here to login", root_url
diff --git a/app/views/notify/new_user_email.text.erb b/app/views/notify/new_user_email.text.erb
index 777930a2803..70fe0e0736a 100644
--- a/app/views/notify/new_user_email.text.erb
+++ b/app/views/notify/new_user_email.text.erb
@@ -3,10 +3,11 @@ Hi <%= @user.name %>!
The Administrator created an account for you. Now you are a member of company GitLab application.
login.................. <%= @user.email %>
-<% unless Gitlab.config.gitlab.signup_enabled %>
+<% if @user.created_by_id %>
password............... <%= @password %>
+
+ You will be forced to change this password immediately after login.
<% end %>
-Please change your password immediately after login.
Click here to login: <%= url_for(root_url) %>
diff --git a/app/views/passwords/new.html.haml b/app/views/passwords/new.html.haml
new file mode 100644
index 00000000000..c92424160b3
--- /dev/null
+++ b/app/views/passwords/new.html.haml
@@ -0,0 +1,22 @@
+= form_for @user, url: profile_password_path, method: :post do |f|
+ .light-well.padded
+ %p.slead
+ Please set new password before proceed.
+ %br
+ After successful password update you will be redirected to login screen
+ -if @user.errors.any?
+ .alert.alert-error
+ %ul
+ - @user.errors.full_messages.each do |msg|
+ %li= msg
+
+ .clearfix
+ = f.label :password
+ .input= f.password_field :password, required: true
+ .clearfix
+ = f.label :password_confirmation
+ .input
+ = f.password_field :password_confirmation, required: true
+ .clearfix
+ .input
+ = f.submit 'Set new password', class: "btn btn-create"
diff --git a/app/views/profiles/account.html.haml b/app/views/profiles/account.html.haml
index 09d9ec10e81..9b1354d9f2c 100644
--- a/app/views/profiles/account.html.haml
+++ b/app/views/profiles/account.html.haml
@@ -3,7 +3,7 @@
%fieldset
%legend Social Accounts
.oauth_select_holder
- %p.hint Tip: Click on icon to activate sigin with one of the following services
+ %p.hint Tip: Click on icon to activate signin with one of the following services
- enabled_social_providers.each do |provider|
%span{class: oauth_active_class(provider) }
= link_to authbutton(provider, 32), omniauth_authorize_path(User, provider)
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index d4793da8987..96870924371 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -1,5 +1,5 @@
.profile_avatar_holder
- = image_tag gravatar_icon(@user.email, 90)
+ = image_tag gravatar_icon(@user.email, 90), alt: ''
%h3.page_title
= @user.name
%br
diff --git a/app/views/projects/_settings_nav.html.haml b/app/views/projects/_settings_nav.html.haml
index acaa03f9ad2..346bbd2daf3 100644
--- a/app/views/projects/_settings_nav.html.haml
+++ b/app/views/projects/_settings_nav.html.haml
@@ -5,8 +5,8 @@
Edit
= nav_link(controller: [:team_members, :teams]) do
= link_to project_team_index_path(@project), class: "team-tab tab" do
- %i.icon-user
- Team
+ %i.icon-group
+ Project Members
= nav_link(controller: :deploy_keys) do
= link_to project_deploy_keys_path(@project) do
%span
@@ -14,7 +14,7 @@
= nav_link(controller: :hooks) do
= link_to project_hooks_path(@project) do
%span
- Hooks
+ Web Hooks
= nav_link(controller: :services) do
= link_to project_services_path(@project) do
%span
diff --git a/app/views/projects/teams/available.html.haml b/app/views/projects/teams/available.html.haml
index 29fe8ed25cd..880a02aeaf5 100644
--- a/app/views/projects/teams/available.html.haml
+++ b/app/views/projects/teams/available.html.haml
@@ -10,7 +10,7 @@
.padded
= label_tag :team_id, "Team"
.input= select_tag(:team_id, options_from_collection_for_select(@teams, :id, :name), prompt: "Select team", class: "chosen xxlarge", required: true)
- %p.slead Choose greatest user acces in team you want to assign:
+ %p.slead Choose greatest user access for your team in this project:
.padded
= label_tag :team_ids, "Permission"
.input= select_tag :greatest_project_access, options_for_select(UserTeam.access_roles), {class: "project-access-select chosen span3" }
diff --git a/app/views/repositories/_branch.html.haml b/app/views/repositories/_branch.html.haml
index dd91e14b66b..2115f3c427f 100644
--- a/app/views/repositories/_branch.html.haml
+++ b/app/views/repositories/_branch.html.haml
@@ -12,7 +12,7 @@
%td
= link_to project_commit_path(@project, commit.id), class: 'commit_short_id' do
= commit.short_id
- = image_tag gravatar_icon(commit.author_email), class: "avatar s16"
+ = image_tag gravatar_icon(commit.author_email), class: "avatar s16", alt: ''
%span.light
= gfm escape_once(truncate(commit.title, length: 40))
%span
diff --git a/app/views/repositories/_feed.html.haml b/app/views/repositories/_feed.html.haml
index 6bb75265ffb..faa3ed1746c 100644
--- a/app/views/repositories/_feed.html.haml
+++ b/app/views/repositories/_feed.html.haml
@@ -11,7 +11,7 @@
%div
= link_to project_commits_path(@project, commit.id) do
%code= commit.short_id
- = image_tag gravatar_icon(commit.author_email), class: "", width: 16
+ = image_tag gravatar_icon(commit.author_email), class: "", width: 16, alt: ''
= gfm escape_once(truncate(commit.title, length: 40))
%td
%span.pull-right.cgray
diff --git a/app/views/repositories/stats.html.haml b/app/views/repositories/stats.html.haml
index 6d1fb4686ea..fc8ccfe458d 100644
--- a/app/views/repositories/stats.html.haml
+++ b/app/views/repositories/stats.html.haml
@@ -19,7 +19,7 @@
%ol.styled
- @stats.authors[0...50].each do |author|
%li
- = image_tag gravatar_icon(author.email, 16), class: 'avatar s16'
+ = image_tag gravatar_icon(author.email, 16), class: 'avatar s16', alt: ''
= author.name
%small.light= author.email
.pull-right
diff --git a/app/views/snippets/_blob.html.haml b/app/views/snippets/_blob.html.haml
index 6f62ea05205..c538da0bee5 100644
--- a/app/views/snippets/_blob.html.haml
+++ b/app/views/snippets/_blob.html.haml
@@ -6,6 +6,7 @@
.btn-group.tree-btn-group.pull-right
- if @snippet.author == current_user
= link_to "Edit", edit_snippet_path(@snippet), class: "btn btn-tiny", title: 'Edit Snippet'
+ = link_to "Delete", snippet_path(@snippet), method: :delete, confirm: "Are you sure?", class: "btn btn-tiny", title: 'Delete Snippet'
= link_to "Raw", raw_snippet_path(@snippet), class: "btn btn-tiny", target: "_blank"
.file_content.code
- unless @snippet.content.empty?
diff --git a/app/views/snippets/_snippet.html.haml b/app/views/snippets/_snippet.html.haml
index 194eb051ee3..a013cdfe16a 100644
--- a/app/views/snippets/_snippet.html.haml
+++ b/app/views/snippets/_snippet.html.haml
@@ -25,6 +25,6 @@
= "##{snippet.id}"
%span.light
by
- = image_tag gravatar_icon(snippet.author_email), class: "avatar avatar-inline s16"
+ = image_tag gravatar_icon(snippet.author_email), class: "avatar avatar-inline s16", alt: ''
= snippet.author_name
diff --git a/app/views/snippets/current_user_index.html.haml b/app/views/snippets/current_user_index.html.haml
index 912f4c77a4b..cf5c3084dc4 100644
--- a/app/views/snippets/current_user_index.html.haml
+++ b/app/views/snippets/current_user_index.html.haml
@@ -1,8 +1,11 @@
%h3.page_title
My Snippets
%small share code pastes with others out of git repository
- = link_to new_snippet_path, class: "btn btn-small add_new pull-right", title: "New Snippet" do
- Add new snippet
+ .pull-right
+ = link_to new_snippet_path, class: "btn btn-small add_new grouped btn-primary", title: "New Snippet" do
+ Add new snippet
+ = link_to snippets_path, class: "btn btn-small grouped" do
+ Discover snippets
%hr
diff --git a/app/views/snippets/index.html.haml b/app/views/snippets/index.html.haml
index 97f7b39877e..4301f90f9d6 100644
--- a/app/views/snippets/index.html.haml
+++ b/app/views/snippets/index.html.haml
@@ -1,8 +1,12 @@
%h3.page_title
Public snippets
%small share code pastes with others out of git repository
- = link_to new_snippet_path, class: "btn btn-small add_new pull-right", title: "New Snippet" do
- Add new snippet
+
+ .pull-right
+ = link_to new_snippet_path, class: "btn btn-small add_new grouped btn-primary", title: "New Snippet" do
+ Add new snippet
+ = link_to user_snippets_path(current_user), class: "btn btn-small grouped" do
+ My snippets
%hr
.row
diff --git a/app/views/snippets/show.html.haml b/app/views/snippets/show.html.haml
index f425c4bd51e..ac6daed56b6 100644
--- a/app/views/snippets/show.html.haml
+++ b/app/views/snippets/show.html.haml
@@ -1,8 +1,8 @@
%h3.page_title
- if @snippet.private?
- %i.icon-lock.cgreen
+ %i{:class => "icon-lock cgreen has_bottom_tooltip", "data-original-title" => "Private snippet"}
- else
- %i.icon-globe.cblue
+ %i{:class => "icon-globe cblue has_bottom_tooltip", "data-original-title" => "Public snippet"}
= @snippet.title
diff --git a/app/views/team_members/_team_member.html.haml b/app/views/team_members/_team_member.html.haml
index d829a79213c..d61513fa03a 100644
--- a/app/views/team_members/_team_member.html.haml
+++ b/app/views/team_members/_team_member.html.haml
@@ -4,7 +4,7 @@
.row
.span4
= link_to user, title: user.name, class: "dark" do
- = image_tag gravatar_icon(user.email, 32), class: "avatar s32"
+ = image_tag gravatar_icon(user.email, 32), class: "avatar s32", alt: ''
%strong= truncate(user.name, lenght: 40)
%br
%small.cgray= user.username
diff --git a/app/views/team_members/index.html.haml b/app/views/team_members/index.html.haml
index 3132fd5ca8a..c0f7ee4330d 100644
--- a/app/views/team_members/index.html.haml
+++ b/app/views/team_members/index.html.haml
@@ -1,6 +1,6 @@
= render "projects/settings_nav"
%h3.page_title
- Team Members
+ Project Members
(#{@project.users.count})
%small
Read more about project permissions
diff --git a/app/views/teams/edit.html.haml b/app/views/teams/edit.html.haml
index c9d573ea7e4..7f2273ee26c 100644
--- a/app/views/teams/edit.html.haml
+++ b/app/views/teams/edit.html.haml
@@ -5,8 +5,9 @@
= link_to 'Projects', '#tab-projects', 'data-toggle' => 'tab'
%li
= link_to 'Edit Team', '#tab-edit', 'data-toggle' => 'tab'
- %li
- = link_to 'Remove', '#tab-remove', 'data-toggle' => 'tab'
+ - if can? current_user, :admin_user_team, @team
+ %li
+ = link_to 'Remove', '#tab-remove', 'data-toggle' => 'tab'
.span9
.tab-content
diff --git a/app/views/teams/members/_member.html.haml b/app/views/teams/members/_member.html.haml
index 17096d2f1cd..19591db03e2 100644
--- a/app/views/teams/members/_member.html.haml
+++ b/app/views/teams/members/_member.html.haml
@@ -4,7 +4,7 @@
.row
.span3
= link_to user_path(user.username), title: user.name, class: "dark" do
- = image_tag gravatar_icon(user.email, 40), class: "avatar s32"
+ = image_tag gravatar_icon(user.email, 40), class: "avatar s32", alt: ''
= link_to user_path(user.username), title: user.name, class: "dark" do
%strong= truncate(user.name, lenght: 40)
%br
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 10bd90b166a..548458e0d3b 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -1,7 +1,7 @@
.row
.span8
%h3.page_title
- = image_tag gravatar_icon(@user.email, 90), class: "avatar s90"
+ = image_tag gravatar_icon(@user.email, 90), class: "avatar s90", alt: ''
= @user.name
- if @user == current_user
.pull-right
diff --git a/config/environments/production.rb b/config/environments/production.rb
index 183b7ae5b70..7e02c75e562 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -52,7 +52,7 @@ Gitlab::Application.configure do
# config.action_mailer.raise_delivery_errors = false
# Enable threaded mode
- config.threadsafe!
+ config.threadsafe! unless $rails_rake_task
# Enable locale fallbacks for I18n (makes lookups for any locale fall back to
# the I18n.default_locale when a translation can not be found)
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index a612102ab1c..ad24ba97c6f 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -1,4 +1,4 @@
-# # # # # # # # # # # # # # # # # #
+# # # # # # # # # # # # # # # # # #
# GitLab application config file #
# # # # # # # # # # # # # # # # # #
#
@@ -18,6 +18,7 @@ production: &base
host: localhost
port: 80
https: false
+ # WARNING: This feature is no longer supported
# Uncomment and customize to run in non-root path
# Note that ENV['RAILS_RELATIVE_URL_ROOT'] in config/puma.rb may need to be changed
# relative_url_root: /gitlab
@@ -37,7 +38,7 @@ production: &base
# default_can_create_group: false # default: true
# default_can_create_team: false # default: true
# username_changing_enabled: false # default: true - User can change her username/namespace
-
+
## Users management
# signup_enabled: true # default: false - Account passwords are not sent via the email if signup is enabled.
@@ -168,12 +169,12 @@ production: &base
# 4. Extra customization
# ==========================
- extra:
+ extra:
## Google analytics. Uncomment if you want it
# google_analytics_id: '_your_tracking_id'
## Text under sign-in page (Markdown enabled)
- # sign_in_text: |
+ # sign_in_text: |
# ![Company Logo](http://www.companydomain.com/logo.png)
# [Learn more about CompanyName](http://www.companydomain.com/)
diff --git a/config/puma.rb.example b/config/puma.rb.example
index ad5e3e23501..08eace369fd 100644
--- a/config/puma.rb.example
+++ b/config/puma.rb.example
@@ -93,6 +93,13 @@ bind "unix://#{application_path}/tmp/sockets/gitlab.socket"
#
# workers 2
+# GitLab cluster mode recommendations
+# If you have more than 1 GB RAM, uncomment one of the following lines:
+#
+# workers 2 # if you have at least 1.5 GB RAM
+# workers 3 # if you have at least 2 GB RAM
+# workers 4 # if you have at least 2.5 GB RAM
+
# Code to run when a worker boots to setup the process before booting
# the app.
#
diff --git a/config/routes.rb b/config/routes.rb
index 6f72e2cb186..f545ad82f77 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -123,6 +123,7 @@ Gitlab::Application.routes.draw do
end
resource :notifications
+ resource :password
end
resources :keys
@@ -318,7 +319,10 @@ Gitlab::Application.routes.draw do
end
end
- resources :notes, only: [:index, :create, :destroy] do
+ resources :notes, only: [:index, :create, :destroy, :update] do
+ member do
+ delete :delete_attachment
+ end
collection do
post :preview
end
diff --git a/config/unicorn.rb.example b/config/unicorn.rb.example
new file mode 100644
index 00000000000..00c509dd829
--- /dev/null
+++ b/config/unicorn.rb.example
@@ -0,0 +1,102 @@
+# Sample verbose configuration file for Unicorn (not Rack)
+#
+# This configuration file documents many features of Unicorn
+# that may not be needed for some applications. See
+# http://unicorn.bogomips.org/examples/unicorn.conf.minimal.rb
+# for a much simpler configuration file.
+#
+# See http://unicorn.bogomips.org/Unicorn/Configurator.html for complete
+# documentation.
+
+# Use at least one worker per core if you're on a dedicated server,
+# more will usually help for _short_ waits on databases/caches.
+worker_processes 2
+
+# Since Unicorn is never exposed to outside clients, it does not need to
+# run on the standard HTTP port (80), there is no reason to start Unicorn
+# as root unless it's from system init scripts.
+# If running the master process as root and the workers as an unprivileged
+# user, do this to switch euid/egid in the workers (also chowns logs):
+# user "unprivileged_user", "unprivileged_group"
+
+# Help ensure your application will always spawn in the symlinked
+# "current" directory that Capistrano sets up.
+working_directory "/home/git/gitlab/current" # available in 0.94.0+
+
+# listen on both a Unix domain socket and a TCP port,
+# we use a shorter backlog for quicker failover when busy
+listen "/home/git/gitlab/tmp/sockets/gitlab.socket", :backlog => 64
+listen 8080, :tcp_nopush => true
+
+# nuke workers after 30 seconds instead of 60 seconds (the default)
+timeout 30
+
+# feel free to point this anywhere accessible on the filesystem
+pid "/home/git/gitlab/tmp/pids/unicorn.pid"
+
+# By default, the Unicorn logger will write to stderr.
+# Additionally, ome applications/frameworks log to stderr or stdout,
+# so prevent them from going to /dev/null when daemonized here:
+stderr_path "/home/git/gitlab/log/unicorn.stderr.log"
+stdout_path "/home/git/gitlab/log/unicorn.stdout.log"
+
+# combine Ruby 2.0.0dev or REE with "preload_app true" for memory savings
+# http://rubyenterpriseedition.com/faq.html#adapt_apps_for_cow
+preload_app true
+GC.respond_to?(:copy_on_write_friendly=) and
+ GC.copy_on_write_friendly = true
+
+# Enable this flag to have unicorn test client connections by writing the
+# beginning of the HTTP headers before calling the application. This
+# prevents calling the application for connections that have disconnected
+# while queued. This is only guaranteed to detect clients on the same
+# host unicorn runs on, and unlikely to detect disconnects even on a
+# fast LAN.
+check_client_connection false
+
+before_fork do |server, worker|
+ # the following is highly recomended for Rails + "preload_app true"
+ # as there's no need for the master process to hold a connection
+ defined?(ActiveRecord::Base) and
+ ActiveRecord::Base.connection.disconnect!
+
+ # The following is only recommended for memory/DB-constrained
+ # installations. It is not needed if your system can house
+ # twice as many worker_processes as you have configured.
+ #
+ # # This allows a new master process to incrementally
+ # # phase out the old master process with SIGTTOU to avoid a
+ # # thundering herd (especially in the "preload_app false" case)
+ # # when doing a transparent upgrade. The last worker spawned
+ # # will then kill off the old master process with a SIGQUIT.
+ # old_pid = "#{server.config[:pid]}.oldbin"
+ # if old_pid != server.pid
+ # begin
+ # sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
+ # Process.kill(sig, File.read(old_pid).to_i)
+ # rescue Errno::ENOENT, Errno::ESRCH
+ # end
+ # end
+ #
+ # Throttle the master from forking too quickly by sleeping. Due
+ # to the implementation of standard Unix signal handlers, this
+ # helps (but does not completely) prevent identical, repeated signals
+ # from being lost when the receiving process is busy.
+ # sleep 1
+end
+
+after_fork do |server, worker|
+ # per-process listener ports for debugging/admin/migrations
+ # addr = "127.0.0.1:#{9293 + worker.nr}"
+ # server.listen(addr, :tries => -1, :delay => 5, :tcp_nopush => true)
+
+ # the following is *required* for Rails + "preload_app true",
+ defined?(ActiveRecord::Base) and
+ ActiveRecord::Base.establish_connection
+
+ # if preload_app is true, then you may also want to check and
+ # restart any other shared sockets/descriptors such as Memcached,
+ # and Redis. TokyoCabinet file handles are safe to reuse
+ # between any number of forked children (assuming your kernel
+ # correctly implements pread()/pwrite() system calls)
+end
diff --git a/db/fixtures/development/09_issues.rb b/db/fixtures/development/09_issues.rb
index d13d520e3dd..627579721d0 100644
--- a/db/fixtures/development/09_issues.rb
+++ b/db/fixtures/development/09_issues.rb
@@ -11,7 +11,7 @@ Gitlab::Seeder.quiet do
next unless user
user_id = user.id
- IssueObserver.current_user = user
+ Thread.current[:current_user] = user
Issue.seed(:id, [{
id: i,
diff --git a/db/fixtures/development/10_merge_requests.rb b/db/fixtures/development/10_merge_requests.rb
index d122d96235e..0a8d67d4461 100644
--- a/db/fixtures/development/10_merge_requests.rb
+++ b/db/fixtures/development/10_merge_requests.rb
@@ -17,7 +17,8 @@ Gitlab::Seeder.quiet do
next if branches.uniq.size < 2
user_id = user.id
- MergeRequestObserver.current_user = user
+ Thread.current[:current_user] = user
+
MergeRequest.seed(:id, [{
id: i,
source_branch: branches.first,
diff --git a/db/fixtures/production/001_admin.rb b/db/fixtures/production/001_admin.rb
index f119694d11d..632f6107b33 100644
--- a/db/fixtures/production/001_admin.rb
+++ b/db/fixtures/production/001_admin.rb
@@ -3,7 +3,8 @@ admin = User.create(
name: "Administrator",
username: 'root',
password: "5iveL!fe",
- password_confirmation: "5iveL!fe"
+ password_confirmation: "5iveL!fe",
+ password_expires_at: Time.now
)
admin.projects_limit = 10000
diff --git a/db/migrate/20130326142630_add_index_to_users_authentication_token.rb b/db/migrate/20130326142630_add_index_to_users_authentication_token.rb
new file mode 100644
index 00000000000..d42ef113738
--- /dev/null
+++ b/db/migrate/20130326142630_add_index_to_users_authentication_token.rb
@@ -0,0 +1,5 @@
+class AddIndexToUsersAuthenticationToken < ActiveRecord::Migration
+ def change
+ add_index :users, :authentication_token, unique: true
+ end
+end
diff --git a/db/migrate/20130613165816_add_password_expires_at_to_users.rb b/db/migrate/20130613165816_add_password_expires_at_to_users.rb
new file mode 100644
index 00000000000..3479c8e64d0
--- /dev/null
+++ b/db/migrate/20130613165816_add_password_expires_at_to_users.rb
@@ -0,0 +1,5 @@
+class AddPasswordExpiresAtToUsers < ActiveRecord::Migration
+ def change
+ add_column :users, :password_expires_at, :datetime
+ end
+end
diff --git a/db/migrate/20130613173246_add_created_by_id_to_user.rb b/db/migrate/20130613173246_add_created_by_id_to_user.rb
new file mode 100644
index 00000000000..615e96eb156
--- /dev/null
+++ b/db/migrate/20130613173246_add_created_by_id_to_user.rb
@@ -0,0 +1,5 @@
+class AddCreatedByIdToUser < ActiveRecord::Migration
+ def change
+ add_column :users, :created_by_id, :integer
+ end
+end
diff --git a/db/migrate/20130614132337_add_improted_to_project.rb b/db/migrate/20130614132337_add_improted_to_project.rb
new file mode 100644
index 00000000000..cc882c3f10a
--- /dev/null
+++ b/db/migrate/20130614132337_add_improted_to_project.rb
@@ -0,0 +1,5 @@
+class AddImprotedToProject < ActiveRecord::Migration
+ def change
+ add_column :projects, :imported, :boolean, default: false, null: false
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 21e553dd612..348272e0832 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended to check this file into your version control system.
-ActiveRecord::Schema.define(:version => 20130522141856) do
+ActiveRecord::Schema.define(:version => 20130614132337) do
create_table "deploy_keys_projects", :force => true do |t|
t.integer "deploy_key_id", :null => false
@@ -172,6 +172,7 @@ ActiveRecord::Schema.define(:version => 20130522141856) do
t.string "issues_tracker_id"
t.boolean "snippets_enabled", :default => true, :null => false
t.datetime "last_activity_at"
+ t.boolean "imported", :default => false, :null => false
end
add_index "projects", ["creator_id"], :name => "index_projects_on_owner_id"
@@ -292,9 +293,12 @@ ActiveRecord::Schema.define(:version => 20130522141856) do
t.string "state"
t.integer "color_scheme_id", :default => 1, :null => false
t.integer "notification_level", :default => 1, :null => false
+ t.datetime "password_expires_at"
+ t.integer "created_by_id"
end
add_index "users", ["admin"], :name => "index_users_on_admin"
+ add_index "users", ["authentication_token"], :name => "index_users_on_authentication_token", :unique => true
add_index "users", ["email"], :name => "index_users_on_email", :unique => true
add_index "users", ["extern_uid", "provider"], :name => "index_users_on_extern_uid_and_provider", :unique => true
add_index "users", ["name"], :name => "index_users_on_name"
diff --git a/doc/api/README.md b/doc/api/README.md
index 9120fe3aea4..9d6e229e4cb 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -69,15 +69,20 @@ When listing resources you can pass the following parameters:
## Contents
-+ [Users](doc/api/users.md)
-+ [Session](doc/api/session.md)
-+ [Projects](doc/api/projects.md)
-+ [Project Snippets](doc/api/project_snippets.md)
-+ [Repositories](doc/api/repositories.md)
-+ [Issues](doc/api/issues.md)
-+ [Milestones](doc/api/milestones.md)
-+ [Notes](doc/api/notes.md)
-+ [Deploy Keys](doc/api/deploy_keys.md)
-+ [System Hooks](doc/api/system_hooks.md)
-+ [Groups](doc/api/groups.md)
-+ [User Teams](doc/api/user_teams.md)
++ [Users](api/users.md)
++ [Session](api/session.md)
++ [Projects](api/projects.md)
++ [Project Snippets](api/project_snippets.md)
++ [Repositories](api/repositories.md)
++ [Issues](api/issues.md)
++ [Milestones](api/milestones.md)
++ [Notes](api/notes.md)
++ [Deploy Keys](api/deploy_keys.md)
++ [System Hooks](api/system_hooks.md)
++ [Groups](api/groups.md)
++ [User Teams](api/user_teams.md)
+
+## Clients
+
++ [php-gitlab-api](https://github.com/m4tthumphrey/php-gitlab-api) - PHP
++ [Ruby Wrapper](https://github.com/NARKOZ/gitlab) - Ruby
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 323c0be63a4..41b6b6add39 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -453,3 +453,28 @@ Parameters:
+ `id` (required) - The ID of the project.
+ `branch` (required) - The name of the branch.
+
+## Admin fork relation
+
+Allows modification of the forked relationship between existing projects. . Available only for admins.
+
+### Create a forked from/to relation between existing projects.
+
+```
+POST /projects/:id/fork/:forked_from_id
+```
+
+Parameters:
+
++ `id` (required) - The ID of the project
++ `forked_from_id:` (required) - The ID of the project that was forked from
+
+### Delete an existing forked from relationship
+
+```
+DELETE /projects/:id/fork
+```
+
+Parameter:
+
++ `id` (required) - The ID of the project \ No newline at end of file
diff --git a/doc/install/databases.md b/doc/install/databases.md
index 2d94aa9b4a3..5ec1d0c6524 100644
--- a/doc/install/databases.md
+++ b/doc/install/databases.md
@@ -11,10 +11,17 @@ GitLab supports the following databases:
# Install the database packages
sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev
+ # Pick a database root password (can be anything), type it and press enter
+ # Retype the database root password and press enter
+
# Login to MySQL
mysql -u root -p
- # Create a user for GitLab. (change $password to a real password)
+ # Type the database root password
+
+ # Create a user for GitLab
+ # do not type the 'mysql>', this is part of the prompt
+ # change $password in the command below to a real password you pick
mysql> CREATE USER 'gitlab'@'localhost' IDENTIFIED BY '$password';
# Create the GitLab production database
@@ -29,6 +36,16 @@ GitLab supports the following databases:
# Try connecting to the new database with the new user
sudo -u git -H mysql -u gitlab -p -D gitlabhq_production
+ # Type the password you replaced $password with earlier
+
+ # You should now see a 'mysql>' prompt
+
+ # Quit the database session
+ mysql> \q
+
+ # You are done installing the database and can go back to the rest of the installation.
+
+
## PostgreSQL
# Install the database packages
diff --git a/doc/install/installation.md b/doc/install/installation.md
index 3b459ad77b5..6cad280acaf 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -36,17 +36,19 @@ The GitLab installation consists of setting up the following components:
`sudo` is not installed on Debian by default. Make sure your system is
up-to-date and install it.
- # run as root
- apt-get update
- apt-get upgrade
- apt-get install sudo
+ # run as root!
+ apt-get update -y
+ apt-get upgrade -y
+ apt-get install sudo -y
**Note:**
-Vim is an editor that is used here whenever there are files that need to be
-edited by hand. But, you can use any editor you like instead.
+During this installation some files will need to be edited manually.
+If you are familiar with vim set it as default editor with the commands below.
+If you are not familiar with vim please skip this and keep using the default editor.
- # Install vim
+ # Install vim and set as default editor
sudo apt-get install -y vim
+ sudo update-alternatives --set editor /usr/bin/vim.basic
Install the required packages:
@@ -55,7 +57,7 @@ Install the required packages:
Make sure you have the right version of Python installed.
# Install Python
- sudo apt-get install python
+ sudo apt-get install -y python
# Make sure that Python is 2.5+ (3.x is not supported at the moment)
python --version
@@ -73,15 +75,17 @@ Make sure you have the right version of Python installed.
mail server. By default, Debian is shipped with exim4 whereas Ubuntu
does not ship with one. The recommended mail server is postfix and you can install it with:
- sudo apt-get install postfix
+ sudo apt-get install -y postfix
+
+Then select 'Internet Site' and press enter to confirm the hostname.
# 2. Ruby
-Remove old 1.8 ruby if present
+Remove the old Ruby 1.8 if present
- sudo apt-get remove ruby1.8
+ sudo apt-get remove -y ruby1.8
-Download and compile it:
+Download Ruby and compile it:
mkdir /tmp/ruby && cd /tmp/ruby
curl --progress http://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.3-p392.tar.gz | tar xz
@@ -92,7 +96,7 @@ Download and compile it:
Install the Bundler Gem:
- sudo gem install bundler
+ sudo gem install bundler --no-ri --no-rdoc
# 3. System Users
@@ -106,28 +110,25 @@ Create a `git` user for Gitlab:
GitLab Shell is a ssh access and repository management software developed specially for GitLab.
- # Login as git
- sudo su git
-
# Go to home directory
cd /home/git
# Clone gitlab shell
- git clone https://github.com/gitlabhq/gitlab-shell.git
+ sudo -u git -H git clone https://github.com/gitlabhq/gitlab-shell.git
cd gitlab-shell
# switch to right version
- git checkout v1.4.0
+ sudo -u git -H git checkout v1.4.0
- cp config.yml.example config.yml
+ sudo -u git -H cp config.yml.example config.yml
# Edit config and replace gitlab_url
# with something like 'http://domain.com/'
- vim config.yml
+ sudo -u git -H editor config.yml
# Do setup
- ./bin/install
+ sudo -u git -H ./bin/install
# 5. Database
@@ -149,11 +150,10 @@ To setup the MySQL/PostgreSQL database and dependencies please see [`doc/install
cd /home/git/gitlab
# Checkout to stable release
- sudo -u git -H git checkout 5-2-stable
+ sudo -u git -H git checkout 5-3-stable
**Note:**
-You can change `5-2-stable` to `master` if you want the *bleeding edge* version, but
-do so with caution!
+You can change `5-3-stable` to `master` if you want the *bleeding edge* version, but do so with caution!
## Configure it
@@ -164,7 +164,7 @@ do so with caution!
# Make sure to change "localhost" to the fully-qualified domain name of your
# host serving GitLab where necessary
- sudo -u git -H vim config/gitlab.yml
+ sudo -u git -H editor config/gitlab.yml
# Make sure GitLab can write to the log/ and tmp/ directories
sudo chown -R git log/
@@ -190,7 +190,7 @@ do so with caution!
# Enable cluster mode if you expect to have a high load instance
# Ex. change amount of workers to 3 for 2GB RAM server
- sudo -u git -H vim config/puma.rb
+ sudo -u git -H editor config/puma.rb
# Configure Git global settings for git user, useful when editing via web
# Edit user.email according to what is set in gitlab.yml
@@ -205,10 +205,21 @@ Make sure to edit both `gitlab.yml` and `puma.rb` to match your setup.
# Mysql
sudo -u git cp config/database.yml.mysql config/database.yml
+ or
+
# PostgreSQL
sudo -u git cp config/database.yml.postgresql config/database.yml
-Make sure to update username/password in config/database.yml.
+ # Make sure to update username/password in config/database.yml.
+ # You only need to adapt the production settings (first part).
+ # If you followed the database guide then please do as follows:
+ # Change 'root' to 'gitlab'
+ # Change 'secure password' with the value you have given to $password
+ # You can keep the double quotes around the password
+ sudo -u git -H editor config/database.yml
+
+ # Make config/database.yml readable to git only
+ sudo -u git -H chmod o-rwx config/database.yml
## Install Gems
@@ -216,17 +227,21 @@ Make sure to update username/password in config/database.yml.
sudo gem install charlock_holmes --version '0.6.9.4'
- # For MySQL (note, the option says "without")
- sudo -u git -H bundle install --deployment --without development test postgres
+ # For MySQL (note, the option says "without ... postgres")
+ sudo -u git -H bundle install --deployment --without development test postgres unicorn aws
- # Or for PostgreSQL
- sudo -u git -H bundle install --deployment --without development test mysql
+ # Or for PostgreSQL (note, the option says "without ... mysql")
+ sudo -u git -H bundle install --deployment --without development test mysql unicorn aws
## Initialize Database and Activate Advanced Features
sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production
+ # Type 'yes' to create the database.
+
+ # When done you see 'Administrator account created:'
+
## Install Init Script
@@ -269,7 +284,7 @@ If you can't or don't want to use Nginx as your web server, have a look at the
[`Advanced Setup Tips`](./installation.md#advanced-setup-tips) section.
## Installation
- sudo apt-get install nginx
+ sudo apt-get install -y nginx
## Site Configuration
@@ -280,11 +295,9 @@ Download an example site config:
Make sure to edit the config file to match your setup:
- # **YOUR_SERVER_FQDN** to the fully-qualified
- # domain name of your host serving GitLab. Also, replace
- # the 'listen' line with the following:
- # listen 80 default_server; # e.g., listen 192.168.1.1:80;
- sudo vim /etc/nginx/sites-available/gitlab
+ # Change YOUR_SERVER_FQDN to the fully-qualified
+ # domain name of your host serving GitLab.
+ sudo editor /etc/nginx/sites-available/gitlab
## Restart
@@ -344,10 +357,10 @@ GitLab uses [Omniauth](http://www.omniauth.org/) for authentication and already
These steps are fairly general and you will need to figure out the exact details from the Omniauth provider's documentation.
-* Add `gem "omniauth-your-auth-provider"` to the [Gemfile](https://github.com/gitlabhq/gitlabhq/blob/5-2-stable/Gemfile#L18)
+* Add `gem "omniauth-your-auth-provider"` to the [Gemfile](https://github.com/gitlabhq/gitlabhq/blob/5-3-stable/Gemfile#L18)
* Run `sudo -u git -H bundle install` to install the new gem(s)
-* Add provider specific configuration options to your `config/gitlab.yml` (you can use the [auth providers section of the example config](https://github.com/gitlabhq/gitlabhq/blob/5-2-stable/config/gitlab.yml.example#L53) as a reference)
-* Add icons for the new provider into the [vendor/assets/images/authbuttons](https://github.com/gitlabhq/gitlabhq/tree/5-2-stable/vendor/assets/images/authbuttons) directory (you can find some more popular ones over at https://github.com/intridea/authbuttons)
+* Add provider specific configuration options to your `config/gitlab.yml` (you can use the [auth providers section of the example config](https://github.com/gitlabhq/gitlabhq/blob/5-3-stable/config/gitlab.yml.example#L53) as a reference)
+* Add icons for the new provider into the [vendor/assets/images/authbuttons](https://github.com/gitlabhq/gitlabhq/tree/5-3-stable/vendor/assets/images/authbuttons) directory (you can find some more popular ones over at https://github.com/intridea/authbuttons)
* Restart GitLab
### Examples
diff --git a/doc/markdown/markdown.md b/doc/markdown/markdown.md
new file mode 100644
index 00000000000..3ff3ab9a5ae
--- /dev/null
+++ b/doc/markdown/markdown.md
@@ -0,0 +1,451 @@
+
+----------------------------------------------
+
+Table of Contents
+=================
+
+----------------------------------------------
+
+[GitLab Flavored Markdown](#toc_3)
+-------------------------------
+[Newlines](#toc_4)
+[Multiple underscores in words](#toc_5)
+[URL autolinking](#toc_6)
+[Code and Syntax Highlighting](#toc_7)
+[Emoji](#toc_8)
+[Special GitLab references](#toc_9)
+
+
+
+[Standard Markdown](#toc_10)
+------------------------------
+[Headers](#toc_11)
+[Emphasis](#toc_20)
+[Lists](#toc_21)
+[Links](#toc_22)
+[Images](#toc_23)
+[Blockquotes](#toc_24)
+[Inline HTML](#toc_25)
+[Horizontal Rule](#toc_26)
+[Line Breaks](#toc_27)
+
+[References](#toc_28)
+---------------------
+
+----------------------------------------------
+
+<a name="gfm" />
+GitLab Flavored Markdown (GFM)
+==============================
+For GitLab we developed something we call "GitLab Flavored Markdown" (GFM). It extends the standard Markdown in a few significant ways to add some useful functionality.
+
+You can use GFM in
+
+* commit messages
+* comments
+* wall posts
+* issues
+* merge requests
+* milestones
+* wiki pages
+
+<a name="newlines" />
+Newlines
+--------
+The biggest difference that GFM introduces is in the handling of linebreaks. With traditional Markdown you can hard wrap paragraphs of text and they will be combined into a single paragraph. We find this to be the cause of a huge number of unintentional formatting errors. GFM treats newlines in paragraph-like content as real line breaks, which is probably what you intended.
+
+The next paragraph contains two phrases separated by a single newline character:
+
+ Roses are red
+ Violets are blue
+
+Roses are red
+Violets are blue
+
+<a name="underscores" />
+Multiple underscores in words
+-----------------------------
+It is not reasonable to italicize just _part_ of a word, especially when you're dealing with code and names that often appear with multiple underscores. Therefore, GFM ignores multiple underscores in words.
+
+ perform_complicated_task
+ do_this_and_do_that_and_another_thing
+
+perform_complicated_task
+do_this_and_do_that_and_another_thing
+
+<a name="autolink" />
+URL autolinking
+---------------
+GFM will autolink standard URLs you copy and paste into your text.
+So if you want to link to a URL (instead of a textural link), you can simply put the URL in verbatim and it will be turned into a link to that URL.
+
+ http://www.google.com
+
+http://www.google.com
+
+<a name="code"/>
+## Code and Syntax Highlighting
+
+Blocks of code are either fenced by lines with three back-ticks <code>```</code>, or are indented with four spaces. Only the fenced code blocks support syntax highlighting.
+
+
+```no-highlight
+Inline `code` has `back-ticks around` it.
+```
+
+Inline `code` has `back-ticks around` it.
+
+Example:
+
+ ```javascript
+ var s = "JavaScript syntax highlighting";
+ alert(s);
+ ```
+
+ ```python
+ def function():
+ #indenting works just fine in the fenced code block
+ s = "Python syntax highlighting"
+ print s
+ ```
+
+ ```ruby
+ require 'redcarpet'
+ markdown = Redcarpet.new("Hello World!")
+ puts markdown.to_html
+ ```
+
+ ```
+ No language indicated, so no syntax highlighting.
+ s = "There is no highlighting for this."
+ But let's throw in a <b>tag</b>.
+ ```
+
+becomes:
+
+```javascript
+var s = "JavaScript syntax highlighting";
+alert(s);
+```
+
+```python
+def function():
+ #indenting works just fine in the fenced code block
+ s = "Python syntax highlighting"
+ print s
+```
+
+```ruby
+require 'redcarpet'
+markdown = Redcarpet.new("Hello World!")
+puts markdown.to_html
+```
+
+```
+No language indicated, so no syntax highlighting.
+s = "There is no highlighting for this."
+But let's throw in a <b>tag</b>.
+```
+
+<a name="emoji"/>
+Emoji
+-----
+
+ Sometimes you want to be :cool: and add some :sparkles: to your :speech_balloon:. Well we have a :gift: for you:
+
+ :exclamation: You can use emoji anywhere GFM is supported. :sunglasses:
+
+ You can use it to point out a :bug: or warn about :monkey:patches. And if someone improves your really :snail: code, send them a :bouquet: or some :candy:. People will :heart: you for that.
+
+ If you are :new: to this, don't be :fearful:. You can easily join the emoji :circus_tent:. All you need to do is to :book: up on the supported codes.
+
+ Consult the [Emoji Cheat Sheet]( "http://www.emoji-cheat-sheet.com/") for a list of all supported emoji codes. :thumbsup:
+
+Sometimes you want to be :cool: and add some :sparkles: to your :speech_balloon:. Well we have a :gift: for you:
+
+:exclamation: You can use emoji anywhere GFM is supported. :sunglasses:
+
+You can use it to point out a :bug: or warn about :monkey:patches. And if someone improves your really :snail: code, send them a :bouquet: or some :candy:. People will :heart: you for that.
+
+If you are :new: to this, don't be :fearful:. You can easily join the emoji :circus_tent:. All you need to do is to :book: up on the supported codes.
+
+Consult the [Emoji Cheat Sheet]( "http://www.emoji-cheat-sheet.com/") for a list of all supported emoji codes. :thumbsup:
+
+<a name="special"/>
+Special GitLab References
+-----
+
+GFM recognized special references.
+You can easily reference e.g. a team member, an issue, or a commit within a project.
+GFM will turn that reference into a link so you can navigate between them easily.
+
+
+GFM will recognize the following:
+
+* @foo : for team members
+* #123 : for issues
+* !123 : for merge requests
+* $123 : for snippets
+* 1234567 : for commits
+* [file](path/to/file) : for file references
+
+<a name="standard"/>
+
+----------------------------------
+# Standard Markdown
+
+----------------------------------
+<a name="headers"/>
+## Headers
+
+```no-highlight
+# H1
+## H2
+### H3
+#### H4
+##### H5
+###### H6
+
+Alternatively, for H1 and H2, an underline-ish style:
+
+Alt-H1
+======
+
+Alt-H2
+------
+```
+
+# H1
+## H2
+### H3
+#### H4
+##### H5
+###### H6
+
+Alternatively, for H1 and H2, an underline-ish style:
+
+Alt-H1
+======
+
+Alt-H2
+------
+
+<a name="emphasis"/>
+## Emphasis
+
+```no-highlight
+Emphasis, aka italics, with *asterisks* or _underscores_.
+
+Strong emphasis, aka bold, with **asterisks** or __underscores__.
+
+Combined emphasis with **asterisks and _underscores_**.
+
+Strikethrough uses two tildes. ~~Scratch this.~~
+```
+
+Emphasis, aka italics, with *asterisks* or _underscores_.
+
+Strong emphasis, aka bold, with **asterisks** or __underscores__.
+
+Combined emphasis with **asterisks and _underscores_**.
+
+Strikethrough uses two tildes. ~~Scratch this.~~
+
+
+<a name="lists"/>
+## Lists
+
+```no-highlight
+1. First ordered list item
+2. Another item
+ * Unordered sub-list.
+1. Actual numbers don't matter, just that it's a number
+ 1. Ordered sub-list
+4. And another item.
+
+ Some text that should be aligned with the above item.
+
+* Unordered list can use asterisks
+- Or minuses
++ Or pluses
+```
+
+1. First ordered list item
+2. Another item
+ * Unordered sub-list.
+1. Actual numbers don't matter, just that it's a number
+ 1. Ordered sub-list
+4. And another item.
+
+ Some text that should be aligned with the above item.
+
+* Unordered list can use asterisks
+- Or minuses
++ Or pluses
+
+<a name="links"/>
+## Links
+
+There are two ways to create links.
+
+ [I'm an inline-style link](https://www.google.com)
+
+ [I'm a reference-style link][Arbitrary case-insensitive reference text]
+
+ [I'm a relative reference to a repository file](../blob/master/LICENSE)
+
+ [You can use numbers for reference-style link definitions][1]
+
+ Or leave it empty and use the [link text itself][]
+
+ Some text to show that the reference links can follow later.
+
+ [arbitrary case-insensitive reference text]: https://www.mozilla.org
+ [1]: http://slashdot.org
+ [link text itself]: http://www.reddit.com
+
+[I'm an inline-style link](https://www.google.com)
+
+[I'm a reference-style link][Arbitrary case-insensitive reference text]
+
+[I'm a relative reference to a repository file](../blob/master/LICENSE)
+
+[You can use numbers for reference-style link definitions][1]
+
+Or leave it empty and use the [link text itself][]
+
+Some text to show that the reference links can follow later.
+
+[arbitrary case-insensitive reference text]: https://www.mozilla.org
+[1]: http://slashdot.org
+[link text itself]: http://www.reddit.com
+
+<a name="images"/>
+## Images
+
+ Here's our logo (hover to see the title text):
+
+ Inline-style:
+ ![alt text](/assets/logo-white.png "Logo Title Text 1")
+
+ Reference-style:
+ ![alt text][logo]
+
+ [logo]: /assets/logo-white.png "Logo Title Text 2"
+
+Here's our logo (hover to see the title text):
+
+Inline-style:
+![alt text](/assets/logo-white.png "Logo Title Text 1")
+
+Reference-style:
+![alt text][logo]
+
+[logo]: /assets/logo-white.png "Logo Title Text 2"
+
+<a name="blockquotes"/>
+## Blockquotes
+
+```no-highlight
+> Blockquotes are very handy in email to emulate reply text.
+> This line is part of the same quote.
+
+Quote break.
+
+> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **Markdown** into a blockquote.
+```
+
+> Blockquotes are very handy in email to emulate reply text.
+> This line is part of the same quote.
+
+Quote break.
+
+> This is a very long line that will still be quoted properly when it wraps. Oh boy let's keep writing to make sure this is long enough to actually wrap for everyone. Oh, you can *put* **Markdown** into a blockquote.
+
+<a name="html"/>
+## Inline HTML
+
+You can also use raw HTML in your Markdown, and it'll mostly work pretty well.
+
+```no-highlight
+<dl>
+ <dt>Definition list</dt>
+ <dd>Is something people use sometimes.</dd>
+
+ <dt>Markdown in HTML</dt>
+ <dd>Does *not* work **very** well. Use HTML <em>tags</em>.</dd>
+</dl>
+```
+
+<dl>
+ <dt>Definition list</dt>
+ <dd>Is something people use sometimes.</dd>
+
+ <dt>Markdown in HTML</dt>
+ <dd>Does *not* work **very** well. Use HTML <em>tags</em>.</dd>
+</dl>
+
+<a name="hr"/>
+## Horizontal Rule
+
+```
+Three or more...
+
+---
+
+Hyphens
+
+***
+
+Asterisks
+
+___
+
+Underscores
+```
+
+Three or more...
+
+---
+
+Hyphens
+
+***
+
+Asterisks
+
+___
+
+Underscores
+
+<a name="lines"/>
+## Line Breaks
+
+My basic recommendation for learning how line breaks work is to experiment and discover -- hit &lt;Enter&gt; once (i.e., insert one newline), then hit it twice (i.e., insert two newlines), see what happens. You'll soon learn to get what you want. "Markdown Toggle" is your friend.
+
+Here are some things to try out:
+
+```
+Here's a line for us to start with.
+
+This line is separated from the one above by two newlines, so it will be a *separate paragraph*.
+
+This line is also a separate paragraph, but...
+This line is only separated by a single newline, so it's a separate line in the *same paragraph*.
+```
+
+Here's a line for us to start with.
+
+This line is separated from the one above by two newlines, so it will be a *separate paragraph*.
+
+This line is also begins a separate paragraph, but...
+This line is only separated by a single newline, so it's a separate line in the *same paragraph*.
+
+------------
+
+<a name="references"/>
+## References
+
+* This document leveraged heavily from the [Markdown-Cheatsheet](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet).
+* The [Markdown Syntax Guide](http://daringfireball.net/projects/markdown/syntax) at Daring Fireball is an excellent resource for a detailed explanation of standard markdown.
+* [Dillinger.io](http://dillinger.io) is a handy tool for testing standard markdown.
diff --git a/doc/update/5.0-to-5.1.md b/doc/update/5.0-to-5.1.md
index 2042b27f5dd..45fc3436ebe 100644
--- a/doc/update/5.0-to-5.1.md
+++ b/doc/update/5.0-to-5.1.md
@@ -41,6 +41,7 @@ sudo -u git -H cp config/puma.rb.example config/puma.rb
sudo -u git -H bundle install --without development test postgres --deployment
sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
sudo -u git -H bundle exec rake migrate_merge_requests RAILS_ENV=production
+sudo -u git -H bundle exec rake assets:precompile RAILS_ENV=production
```
### 5. Update init.d script with a new one
diff --git a/doc/update/5.1-to-5.2.md b/doc/update/5.1-to-5.2.md
index 3f874555f49..23de2d99c58 100644
--- a/doc/update/5.1-to-5.2.md
+++ b/doc/update/5.1-to-5.2.md
@@ -52,17 +52,26 @@ sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
### 6. Update Init script
```bash
+cd /home/git/gitlab
sudo rm /etc/init.d/gitlab
-sudo curl --output /etc/init.d/gitlab https://raw.github.com/gitlabhq/gitlabhq/5-2-stable/lib/support/init.d/gitlab
+sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
sudo chmod +x /etc/init.d/gitlab
```
-### 6. Start application
+### 7. Create uploads directory
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H mkdir public/uploads
+sudo chmod -R u+rwX public/uploads
+```
+
+### 8. Start application
sudo service gitlab start
sudo service nginx restart
-### 7. Check application status
+### 9. Check application status
Check if GitLab and its environment are configured correctly:
diff --git a/doc/update/5.2-to-5.3.md b/doc/update/5.2-to-5.3.md
new file mode 100644
index 00000000000..4d930d84c7d
--- /dev/null
+++ b/doc/update/5.2-to-5.3.md
@@ -0,0 +1,80 @@
+# From 5.2 to 5.3
+
+### 0. Backup
+
+It's useful to make a backup just in case things go south:
+(With MySQL, this may require granting "LOCK TABLES" privileges to the GitLab user on the database version)
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H RAILS_ENV=production bundle exec rake gitlab:backup:create
+```
+
+### 1. Stop server
+
+ sudo service gitlab stop
+
+### 2. Get latest code
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H git fetch
+sudo -u git -H git checkout 5-3-stable
+```
+
+### 3. Install libs, migrations, etc.
+
+```bash
+cd /home/git/gitlab
+
+# MySQL
+sudo -u git -H bundle install --without development test postgres --deployment
+
+#PostgreSQL
+sudo -u git -H bundle install --without development test mysql --deployment
+
+sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
+```
+
+### 4. Update config files
+
+* Make `/home/git/gitlab/config/gitlab.yml` same as https://github.com/gitlabhq/gitlabhq/blob/5-3-stable/config/gitlab.yml.example but with your settings.
+* Make `/home/git/gitlab/config/puma.rb` same as https://github.com/gitlabhq/gitlabhq/blob/5-3-stable/config/puma.rb.example but with your settings.
+
+### 5. Update Init script
+
+```bash
+sudo rm /etc/init.d/gitlab
+sudo curl --output /etc/init.d/gitlab https://raw.github.com/gitlabhq/gitlabhq/5-3-stable/lib/support/init.d/gitlab
+sudo chmod +x /etc/init.d/gitlab
+```
+
+### 6. Start application
+
+ sudo service gitlab start
+ sudo service nginx restart
+
+### 7. Check application status
+
+Check if GitLab and its environment are configured correctly:
+
+ sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
+
+To make sure you didn't miss anything run a more thorough check with:
+
+ sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
+
+If all items are green, then congratulations upgrade complete!
+
+## Things went south? Revert to previous version (5.2)
+
+### 1. Revert the code to the previous version
+Follow the [`upgrade guide from 5.1 to 5.2`](5.1-to-5.2.md), except for the database migration
+(The backup is already migrated to the previous version)
+
+### 2. Restore from the backup:
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H RAILS_ENV=production bundle exec rake gitlab:backup:restore
+```
diff --git a/features/project/issues/milestones.feature b/features/project/issues/milestones.feature
index 50c090cc6a0..2f38acf14d0 100644
--- a/features/project/issues/milestones.feature
+++ b/features/project/issues/milestones.feature
@@ -22,5 +22,3 @@ Feature: Project Milestones
Given the milestone has open and closed issues
And I click link "v2.2"
Then I should see 3 issues
- When I click link "All Issues"
- Then I should see 4 issues
diff --git a/features/steps/project/project_active_tab.rb b/features/steps/project/project_active_tab.rb
index 4c6d890fe4b..5170ab82e8a 100644
--- a/features/steps/project/project_active_tab.rb
+++ b/features/steps/project/project_active_tab.rb
@@ -45,7 +45,7 @@ class ProjectActiveTab < Spinach::FeatureSteps
# Sub Tabs: Home
Given 'I click the "Team" tab' do
- click_link('Team')
+ click_link('Project Members')
end
Given 'I click the "Attachments" tab' do
@@ -61,7 +61,7 @@ class ProjectActiveTab < Spinach::FeatureSteps
end
Given 'I click the "Hooks" tab' do
- click_link('Hooks')
+ click_link('Web Hooks')
end
Given 'I click the "Deploy Keys" tab' do
@@ -73,7 +73,7 @@ class ProjectActiveTab < Spinach::FeatureSteps
end
Then 'the active sub tab should be Team' do
- ensure_active_sub_tab('Team')
+ ensure_active_sub_tab('Project Members')
end
Then 'the active sub tab should be Attachments' do
@@ -89,7 +89,7 @@ class ProjectActiveTab < Spinach::FeatureSteps
end
Then 'the active sub tab should be Hooks' do
- ensure_active_sub_tab('Hooks')
+ ensure_active_sub_tab('Web Hooks')
end
Then 'the active sub tab should be Deploy Keys' do
diff --git a/features/steps/project/project_merge_requests.rb b/features/steps/project/project_merge_requests.rb
index 6a2f870e276..ea434412bb2 100644
--- a/features/steps/project/project_merge_requests.rb
+++ b/features/steps/project/project_merge_requests.rb
@@ -57,8 +57,8 @@ class ProjectMergeRequests < Spinach::FeatureSteps
And 'I submit new merge request "Wiki Feature"' do
fill_in "merge_request_title", with: "Wiki Feature"
- select "master", from: "merge_request_source_branch"
- select "stable", from: "merge_request_target_branch"
+ select "bootstrap", from: "merge_request_source_branch"
+ select "master", from: "merge_request_target_branch"
click_button "Submit merge request"
end
diff --git a/features/steps/project/project_milestones.rb b/features/steps/project/project_milestones.rb
index fcd590fcab2..c4d0d176f3a 100644
--- a/features/steps/project/project_milestones.rb
+++ b/features/steps/project/project_milestones.rb
@@ -50,12 +50,6 @@ class ProjectMilestones < Spinach::FeatureSteps
end
Then "I should see 3 issues" do
- page.should have_selector('.milestone-issue-filter .well-list li', count: 4)
- page.should have_selector('.milestone-issue-filter .well-list li.hide', count: 1)
- end
-
- Then "I should see 4 issues" do
- page.should have_selector('.milestone-issue-filter .well-list li', count: 4)
- page.should_not have_selector('.milestone-issue-filter .well-list li.hide')
+ page.should have_selector('#tab-issues li', count: 4)
end
end
diff --git a/features/steps/userteams/userteams.rb b/features/steps/userteams/userteams.rb
index 9a86572e1ac..b4b2fb66a50 100644
--- a/features/steps/userteams/userteams.rb
+++ b/features/steps/userteams/userteams.rb
@@ -93,7 +93,7 @@ class Userteams < Spinach::FeatureSteps
Then 'I should see issues from this team assigned to me' do
team = UserTeam.last
team.projects.each do |project|
- project.issues.assigned(current_user).each do |issue|
+ project.issues.assigned_to(current_user).each do |issue|
page.should have_content issue.title
end
end
@@ -121,7 +121,7 @@ class Userteams < Spinach::FeatureSteps
team = UserTeam.last
team.projects.each do |project|
team.members.each do |member|
- project.issues.assigned(member).each do |issue|
+ project.issues.assigned_to(member).each do |issue|
page.should have_content issue.title
end
end
@@ -131,9 +131,7 @@ class Userteams < Spinach::FeatureSteps
Given 'project from team has merge requests assigned to me' do
team = UserTeam.last
team.projects.each do |project|
- team.members.each do |member|
- 3.times { create(:merge_request, assignee: member, project: project) }
- end
+ create(:merge_request, assignee: current_user, project: project)
end
end
@@ -145,10 +143,8 @@ class Userteams < Spinach::FeatureSteps
Then 'I should see merge requests from this team assigned to me' do
team = UserTeam.last
team.projects.each do |project|
- team.members.each do |member|
- project.issues.assigned(member).each do |merge_request|
- page.should have_content merge_request.title
- end
+ project.merge_requests.each do |merge_request|
+ page.should have_content merge_request.title
end
end
end
@@ -156,20 +152,8 @@ class Userteams < Spinach::FeatureSteps
Given 'project from team has merge requests assigned to team members' do
team = UserTeam.last
team.projects.each do |project|
- team.members.each do |member|
- 3.times { create(:merge_request, assignee: member, project: project) }
- end
- end
- end
-
- Then 'I should see merge requests from this team assigned to me' do
- team = UserTeam.last
- team.projects.each do |project|
- team.members.each do |member|
- project.issues.assigned(member).each do |merge_request|
- page.should have_content merge_request.title
- end
- end
+ member = team.members.sample
+ create(:merge_request, assignee: member, project: project)
end
end
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 0d8cac5c8fd..dea5771d6b6 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -25,6 +25,12 @@ module API
expose :id, :url, :created_at
end
+ class ForkedFromProject < Grape::Entity
+ expose :id
+ expose :name, :name_with_namespace
+ expose :path, :path_with_namespace
+ end
+
class Project < Grape::Entity
expose :id, :description, :default_branch, :public, :ssh_url_to_repo, :http_url_to_repo, :web_url
expose :owner, using: Entities::UserBasic
@@ -32,6 +38,7 @@ module API
expose :path, :path_with_namespace
expose :issues_enabled, :merge_requests_enabled, :wall_enabled, :wiki_enabled, :created_at, :last_activity_at
expose :namespace
+ expose :forked_from_project, using: Entities::ForkedFromProject, :if => lambda{ | project, options | project.forked? }
end
class ProjectMember < UserBasic
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 94cf4f2e69f..f857d4133b2 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -5,12 +5,12 @@ module API
end
def user_project
- @project ||= find_project
+ @project ||= find_project(params[:id])
@project || not_found!
end
- def find_project
- project = Project.find_by_id(params[:id]) || Project.find_with_namespace(params[:id])
+ def find_project(id)
+ project = Project.find_by_id(id) || Project.find_with_namespace(id)
if project && can?(current_user, :read_project, project)
project
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 6dc051e4ba2..d5709f5cb59 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -121,6 +121,42 @@ module API
end
+ # Mark this project as forked from another
+ #
+ # Parameters:
+ # id: (required) - The ID of the project being marked as a fork
+ # forked_from_id: (required) - The ID of the project it was forked from
+ # Example Request:
+ # POST /projects/:id/fork/:forked_from_id
+ post ":id/fork/:forked_from_id" do
+ authenticated_as_admin!
+ forked_from_project = find_project(params[:forked_from_id])
+ unless forked_from_project.nil?
+ if user_project.forked_from_project.nil?
+ user_project.create_forked_project_link(forked_to_project_id: user_project.id, forked_from_project_id: forked_from_project.id)
+ else
+ render_api_error!("Project already forked", 409)
+ end
+ else
+ not_found!
+ end
+
+ end
+
+ # Remove a forked_from relationship
+ #
+ # Parameters:
+ # id: (required) - The ID of the project being marked as a fork
+ # Example Request:
+ # DELETE /projects/:id/fork
+ delete ":id/fork" do
+ authenticated_as_admin!
+ unless user_project.forked_project_link.nil?
+ user_project.forked_project_link.destroy
+ end
+ end
+
+
# Get a project team members
#
# Parameters:
diff --git a/lib/gitlab/backend/grack_auth.rb b/lib/gitlab/backend/grack_auth.rb
index 4f3f7b02a5b..e7217c7c7e6 100644
--- a/lib/gitlab/backend/grack_auth.rb
+++ b/lib/gitlab/backend/grack_auth.rb
@@ -1,9 +1,13 @@
require_relative 'shell_env'
-require 'omniauth-ldap'
+require_relative 'grack_ldap'
+require_relative 'grack_helpers'
module Grack
class Auth < Rack::Auth::Basic
- attr_accessor :user, :project
+ include LDAP
+ include Helpers
+
+ attr_accessor :user, :project, :ref, :env
def call(env)
@env = env
@@ -14,42 +18,52 @@ module Grack
@env['PATH_INFO'] = @request.path
@env['SCRIPT_NAME'] = ""
- return render_not_found unless project
- return unauthorized unless project.public || @auth.provided?
- return bad_request if @auth.provided? && !@auth.basic?
-
- if valid?
- if @auth.provided?
- @env['REMOTE_USER'] = @auth.username
- end
- return @app.call(env)
- else
- unauthorized
- end
+ auth!
end
- def valid?
+ private
+
+ def auth!
+ return render_not_found unless project
+
if @auth.provided?
+ return bad_request unless @auth.basic?
+
# Authentication with username and password
login, password = @auth.credentials
- @user = authenticate(login, password)
- return false unless @user
+ @user = authenticate_user(login, password)
- Gitlab::ShellEnv.set_env(@user)
+ if @user
+ Gitlab::ShellEnv.set_env(@user)
+ @env['REMOTE_USER'] = @auth.username
+ else
+ return unauthorized
+ end
+
+ else
+ return unauthorized unless project.public
end
+ if authorized_git_request?
+ @app.call(env)
+ else
+ unauthorized
+ end
+ end
+
+ def authorized_git_request?
# Git upload and receive
if @request.get?
- validate_get_request
+ authorize_request(@request.params['service'])
elsif @request.post?
- validate_post_request
+ authorize_request(File.basename(@request.path))
else
false
end
end
- def authenticate(login, password)
+ def authenticate_user(login, password)
user = User.find_by_email(login) || User.find_by_username(login)
# If the provided login was not a known email or username
@@ -65,34 +79,12 @@ module Grack
end
end
- def ldap_auth(login, password)
- # Check user against LDAP backend if user is not authenticated
- # Only check with valid login and password to prevent anonymous bind results
- return nil unless ldap_conf.enabled && !login.blank? && !password.blank?
-
- ldap = OmniAuth::LDAP::Adaptor.new(ldap_conf)
- ldap_user = ldap.bind_as(
- filter: Net::LDAP::Filter.eq(ldap.uid, login),
- size: 1,
- password: password
- )
-
- User.find_by_extern_uid_and_provider(ldap_user.dn, 'ldap') if ldap_user
- end
-
- def validate_get_request
- validate_request(@request.params['service'])
- end
-
- def validate_post_request
- validate_request(File.basename(@request.path))
- end
-
- def validate_request(service)
- if service == 'git-upload-pack'
+ def authorize_request(service)
+ case service
+ when 'git-upload-pack'
project.public || can?(user, :download_code, project)
- elsif service == 'git-receive-pack'
- action = if project.protected_branch?(current_ref)
+ when'git-receive-pack'
+ action = if project.protected_branch?(ref)
:push_code_to_protected_branches
else
:push_code
@@ -104,49 +96,24 @@ module Grack
end
end
- def can?(object, action, subject)
- abilities.allowed?(object, action, subject)
- end
-
- def current_ref
- if @env["HTTP_CONTENT_ENCODING"] =~ /gzip/
- input = Zlib::GzipReader.new(@request.body).read
- else
- input = @request.body.read
- end
- # Need to reset seek point
- @request.body.rewind
- /refs\/heads\/([\w\.-]+)/n.match(input.force_encoding('ascii-8bit')).to_a.last
- end
-
def project
- unless instance_variable_defined? :@project
- # Find project by PATH_INFO from env
- if m = /^\/([\w\.\/-]+)\.git/.match(@request.path_info).to_a
- @project = Project.find_with_namespace(m.last)
- end
- end
- return @project
+ @project ||= project_by_path(@request.path_info)
end
- PLAIN_TYPE = {"Content-Type" => "text/plain"}
-
- def render_not_found
- [404, PLAIN_TYPE, ["Not Found"]]
+ def ref
+ @ref ||= parse_ref
end
- protected
+ def parse_ref
+ input = if @env["HTTP_CONTENT_ENCODING"] =~ /gzip/
+ Zlib::GzipReader.new(@request.body).read
+ else
+ @request.body.read
+ end
- def abilities
- @abilities ||= begin
- abilities = Six.new
- abilities << Ability
- abilities
- end
- end
-
- def ldap_conf
- @ldap_conf ||= Gitlab.config.ldap
+ # Need to reset seek point
+ @request.body.rewind
+ /refs\/heads\/([\w\.-]+)/n.match(input.force_encoding('ascii-8bit')).to_a.last
end
- end# Auth
-end# Grack
+ end
+end
diff --git a/lib/gitlab/backend/grack_helpers.rb b/lib/gitlab/backend/grack_helpers.rb
new file mode 100644
index 00000000000..5ac9e9f325b
--- /dev/null
+++ b/lib/gitlab/backend/grack_helpers.rb
@@ -0,0 +1,28 @@
+module Grack
+ module Helpers
+ def project_by_path(path)
+ if m = /^\/([\w\.\/-]+)\.git/.match(path).to_a
+ path_with_namespace = m.last
+ path_with_namespace.gsub!(/\.wiki$/, '')
+
+ Project.find_with_namespace(path_with_namespace)
+ end
+ end
+
+ def render_not_found
+ [404, {"Content-Type" => "text/plain"}, ["Not Found"]]
+ end
+
+ def can?(object, action, subject)
+ abilities.allowed?(object, action, subject)
+ end
+
+ def abilities
+ @abilities ||= begin
+ abilities = Six.new
+ abilities << Ability
+ abilities
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/backend/grack_ldap.rb b/lib/gitlab/backend/grack_ldap.rb
new file mode 100644
index 00000000000..45e98fbac1e
--- /dev/null
+++ b/lib/gitlab/backend/grack_ldap.rb
@@ -0,0 +1,24 @@
+require 'omniauth-ldap'
+
+module Grack
+ module LDAP
+ def ldap_auth(login, password)
+ # Check user against LDAP backend if user is not authenticated
+ # Only check with valid login and password to prevent anonymous bind results
+ return nil unless ldap_conf.enabled && !login.blank? && !password.blank?
+
+ ldap = OmniAuth::LDAP::Adaptor.new(ldap_conf)
+ ldap_user = ldap.bind_as(
+ filter: Net::LDAP::Filter.eq(ldap.uid, login),
+ size: 1,
+ password: password
+ )
+
+ User.find_by_extern_uid_and_provider(ldap_user.dn, 'ldap') if ldap_user
+ end
+
+ def ldap_conf
+ @ldap_conf ||= Gitlab.config.ldap
+ end
+ end
+end
diff --git a/lib/gitlab/blacklist.rb b/lib/gitlab/blacklist.rb
index b678a83fabf..59203b2fbd6 100644
--- a/lib/gitlab/blacklist.rb
+++ b/lib/gitlab/blacklist.rb
@@ -3,7 +3,7 @@ module Gitlab
extend self
def path
- %w(admin dashboard groups help profile projects search public assets u s teams merge_requests issues users snippets )
+ %w(admin dashboard groups help profile projects search public assets u s teams merge_requests issues users snippets services)
end
end
end
diff --git a/lib/redcarpet/render/gitlab_html.rb b/lib/redcarpet/render/gitlab_html.rb
index 318adbf1894..d9c2d3b626d 100644
--- a/lib/redcarpet/render/gitlab_html.rb
+++ b/lib/redcarpet/render/gitlab_html.rb
@@ -12,7 +12,7 @@ class Redcarpet::Render::GitlabHTML < Redcarpet::Render::HTML
def block_code(code, language)
options = { options: {encoding: 'utf-8'} }
lexer = Pygments::Lexer.find(language) # language can be an alias
- options.merge!(lexer: lexer.name.downcase) if lexer # downcase is required
+ options.merge!(lexer: lexer.aliases[0].downcase) if lexer # downcase is required
# New lines are placed to fix an rendering issue
# with code wrapped inside <h1> tag for next case:
diff --git a/lib/support/init.d/gitlab b/lib/support/init.d/gitlab
index f4ca07b3676..bb0151d9335 100644
--- a/lib/support/init.d/gitlab
+++ b/lib/support/init.d/gitlab
@@ -51,7 +51,7 @@ start() {
exit 1
else
if [ `whoami` = root ]; then
- execute "rm $SOCKET_PATH/gitlab.socket"
+ execute "rm -f $SOCKET_PATH/gitlab.socket"
execute "RAILS_ENV=production bundle exec puma $DAEMON_OPTS"
execute "mkdir -p $PID_PATH && $START_SIDEKIQ > /dev/null 2>&1 &"
echo "$DESC started"
diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab
index be7a378b3aa..3e929c52990 100644
--- a/lib/support/nginx/gitlab
+++ b/lib/support/nginx/gitlab
@@ -7,7 +7,7 @@ upstream gitlab {
}
server {
- listen YOUR_SERVER_IP:80 default_server; # e.g., listen 192.168.1.1:80; In most cases *:80 is a good idea
+ listen *:80 default_server; # e.g., listen 192.168.1.1:80; In most cases *:80 is a good idea
server_name YOUR_SERVER_FQDN; # e.g., server_name source.example.com;
server_tokens off; # don't show the version number, a security best practice
root /home/git/gitlab/public;
diff --git a/lib/tasks/gitlab/import.rake b/lib/tasks/gitlab/import.rake
index c11284e3d78..2fd7d017db8 100644
--- a/lib/tasks/gitlab/import.rake
+++ b/lib/tasks/gitlab/import.rake
@@ -29,8 +29,6 @@ namespace :gitlab do
# Skip if group or user
next if namespaces.include?(name)
- next if name == 'gitolite-admin'
-
puts "Processing #{repo_path}".yellow
project = Project.find_with_namespace(path)
diff --git a/spec/factories.rb b/spec/factories.rb
index b596f80fa9e..bd2ec6abf62 100644
--- a/spec/factories.rb
+++ b/spec/factories.rb
@@ -1,3 +1,5 @@
+include ActionDispatch::TestProcess
+
FactoryGirl.define do
sequence :sentence, aliases: [:title, :content] do
Faker::Lorem.sentence
@@ -120,6 +122,7 @@ FactoryGirl.define do
factory :note_on_issue, traits: [:on_issue], aliases: [:votable_note]
factory :note_on_merge_request, traits: [:on_merge_request]
factory :note_on_merge_request_diff, traits: [:on_merge_request, :on_diff]
+ factory :note_on_merge_request_with_attachment, traits: [:on_merge_request, :with_attachment]
trait :on_commit do
project factory: :project_with_code
@@ -141,6 +144,10 @@ FactoryGirl.define do
noteable_id 1
noteable_type "Issue"
end
+
+ trait :with_attachment do
+ attachment { fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "image/png") }
+ end
end
factory :event do
diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb
index bec43e5029c..15de101a17a 100644
--- a/spec/features/admin/admin_users_spec.rb
+++ b/spec/features/admin/admin_users_spec.rb
@@ -20,13 +20,10 @@ describe "Admin::Users" do
describe "GET /admin/users/new" do
before do
- @password = "123ABC"
visit new_admin_user_path
fill_in "user_name", with: "Big Bang"
fill_in "user_username", with: "bang"
fill_in "user_email", with: "bigbang@mail.com"
- fill_in "user_password", with: @password
- fill_in "user_password_confirmation", with: @password
end
it "should create new user" do
@@ -57,26 +54,13 @@ describe "Admin::Users" do
end
it "should send valid email to user with email & password" do
- Gitlab.config.gitlab.stub(:signup_enabled).and_return(false)
User.observers.enable :user_observer do
click_button "Create user"
user = User.last
email = ActionMailer::Base.deliveries.last
email.subject.should have_content("Account was created")
email.text_part.body.should have_content(user.email)
- email.text_part.body.should have_content(@password)
- end
- end
-
- it "should send valid email to user with email without password when signup is enabled" do
- Gitlab.config.gitlab.stub(:signup_enabled).and_return(true)
- User.observers.enable :user_observer do
- click_button "Create user"
- user = User.last
- email = ActionMailer::Base.deliveries.last
- email.subject.should have_content("Account was created")
- email.text_part.body.should have_content(user.email)
- email.text_part.body.should_not have_content(@password)
+ email.text_part.body.should have_content('password')
end
end
end
diff --git a/spec/features/notes_on_merge_requests_spec.rb b/spec/features/notes_on_merge_requests_spec.rb
index 24f5437efff..4aa8937926c 100644
--- a/spec/features/notes_on_merge_requests_spec.rb
+++ b/spec/features/notes_on_merge_requests_spec.rb
@@ -3,6 +3,7 @@ require 'spec_helper'
describe "On a merge request", js: true do
let!(:project) { create(:project_with_code) }
let!(:merge_request) { create(:merge_request, project: project) }
+ let!(:note) { create(:note_on_merge_request_with_attachment, project: project) }
before do
login_as :user
@@ -65,11 +66,70 @@ describe "On a merge request", js: true do
within(".js-main-target-form") { should have_css(".js-note-preview", visible: false) }
within(".js-main-target-form") { should have_css(".js-note-text", visible: true) }
end
+ end
+
+ describe "when editing a note", js: true do
+ it "should contain the hidden edit form" do
+ within("#note_#{note.id}") { should have_css(".note-edit-form", visible: false) }
+ end
+
+ describe "editing the note" do
+ before do
+ find('.note').hover
+ find(".js-note-edit").click
+ end
+
+ it "should show the note edit form and hide the note body" do
+ within("#note_#{note.id}") do
+ find(".note-edit-form", visible: true).should be_visible
+ find(".note-text", visible: false).should_not be_visible
+ end
+ end
+
+ it "should reset the edit note form textarea with the original content of the note if cancelled" do
+ find('.note').hover
+ find(".js-note-edit").click
+
+ within(".note-edit-form") do
+ fill_in "note[note]", with: "Some new content"
+ find(".btn-cancel").click
+ find(".js-note-text", visible: false).text.should == note.note
+ end
+ end
+
+ it "appends the edited at time to the note" do
+ find('.note').hover
+ find(".js-note-edit").click
- it "should be removable" do
- find('.note').hover
- find(".js-note-delete").click
- should_not have_css(".note")
+ within(".note-edit-form") do
+ fill_in "note[note]", with: "Some new content"
+ find(".btn-save").click
+ end
+
+ within("#note_#{note.id}") do
+ should have_css(".note-last-update small")
+ find(".note-last-update small").text.should match(/Edited just now/)
+ end
+ end
+ end
+
+ describe "deleting an attachment" do
+ before do
+ find('.note').hover
+ find(".js-note-edit").click
+ end
+
+ it "shows the delete link" do
+ within(".note-attachment") do
+ should have_css(".js-note-attachment-delete")
+ end
+ end
+
+ it "removes the attachment div and resets the edit form" do
+ find(".js-note-attachment-delete").click
+ should_not have_css(".note-attachment")
+ find(".note-edit-form", visible: false).should_not be_visible
+ end
end
end
end
diff --git a/spec/fixtures/dk.png b/spec/fixtures/dk.png
new file mode 100644
index 00000000000..87ce25e877a
--- /dev/null
+++ b/spec/fixtures/dk.png
Binary files differ
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index 84ce7e86d27..d2e1e8a8743 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -15,7 +15,7 @@ describe Notify do
describe 'for new users, the email' do
let(:example_site_path) { root_path }
- let(:new_user) { create(:user, email: 'newguy@example.com') }
+ let(:new_user) { create(:user, email: 'newguy@example.com', created_by_id: 1) }
subject { Notify.new_user_email(new_user.id, new_user.password) }
@@ -32,8 +32,7 @@ describe Notify do
end
it 'contains the new user\'s password' do
- Gitlab.config.gitlab.stub(:signup_enabled).and_return(false)
- should have_body_text /#{new_user.password}/
+ should have_body_text /password/
end
it 'includes a link to the site' do
@@ -61,8 +60,7 @@ describe Notify do
end
it 'should not contain the new user\'s password' do
- Gitlab.config.gitlab.stub(:signup_enabled).and_return(true)
- should_not have_body_text /#{new_user.password}/
+ should_not have_body_text /password/
end
it 'includes a link to the site' do
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 31075149647..a6612af83eb 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -595,4 +595,71 @@ describe API::API do
end
end
end
+
+ describe :fork_admin do
+ let(:project_fork_target) { create(:project) }
+ let(:project_fork_source) { create(:project, public: true) }
+
+ describe "POST /projects/:id/fork/:forked_from_id" do
+ let(:new_project_fork_source) { create(:project, public: true) }
+
+ it "shouldn't available for non admin users" do
+ post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", user)
+ response.status.should == 403
+ end
+
+ it "should allow project to be forked from an existing project" do
+ project_fork_target.forked?.should_not be_true
+ post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
+ response.status.should == 201
+ project_fork_target.reload
+ project_fork_target.forked_from_project.id.should == project_fork_source.id
+ project_fork_target.forked_project_link.should_not be_nil
+ project_fork_target.forked?.should be_true
+ end
+
+ it "should fail if forked_from project which does not exist" do
+ post api("/projects/#{project_fork_target.id}/fork/9999", admin)
+ response.status.should == 404
+ end
+
+ it "should fail with 409 if already forked" do
+ post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
+ project_fork_target.reload
+ project_fork_target.forked_from_project.id.should == project_fork_source.id
+ post api("/projects/#{project_fork_target.id}/fork/#{new_project_fork_source.id}", admin)
+ response.status.should == 409
+ project_fork_target.reload
+ project_fork_target.forked_from_project.id.should == project_fork_source.id
+ project_fork_target.forked?.should be_true
+ end
+ end
+
+ describe "DELETE /projects/:id/fork" do
+
+ it "shouldn't available for non admin users" do
+ delete api("/projects/#{project_fork_target.id}/fork", user)
+ response.status.should == 403
+ end
+
+ it "should make forked project unforked" do
+ post api("/projects/#{project_fork_target.id}/fork/#{project_fork_source.id}", admin)
+ project_fork_target.reload
+ project_fork_target.forked_from_project.should_not be_nil
+ project_fork_target.forked?.should be_true
+ delete api("/projects/#{project_fork_target.id}/fork", admin)
+ response.status.should == 200
+ project_fork_target.reload
+ project_fork_target.forked_from_project.should be_nil
+ project_fork_target.forked?.should_not be_true
+ end
+
+ it "should be idempotent if not forked" do
+ project_fork_target.forked_from_project.should be_nil
+ delete api("/projects/#{project_fork_target.id}/fork", admin)
+ response.status.should == 200
+ project_fork_target.reload.forked_from_project.should be_nil
+ end
+ end
+ end
end
diff --git a/spec/routing/project_routing_spec.rb b/spec/routing/project_routing_spec.rb
index b2b20ef6b5d..0b6528bfc81 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -262,7 +262,7 @@ end
# project_snippet GET /:project_id/snippets/:id(.:format) snippets#show
# PUT /:project_id/snippets/:id(.:format) snippets#update
# DELETE /:project_id/snippets/:id(.:format) snippets#destroy
-describe Project::SnippetsController, "routing" do
+describe SnippetsController, "routing" do
it "to #raw" do
get("/gitlabhq/snippets/1/raw").should route_to('projects/snippets#raw', project_id: 'gitlabhq', id: '1')
end
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index 21e4202f4ee..76501482303 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -19,7 +19,7 @@ describe NotificationService do
describe 'Notes' do
context 'issue note' do
let(:issue) { create(:issue, assignee: create(:user)) }
- let(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id) }
+ let(:note) { create(:note_on_issue, noteable: issue, project_id: issue.project_id, note: '@mention referenced') }
before do
build_team(note.project)
@@ -30,6 +30,7 @@ describe NotificationService do
should_email(@u_watcher.id)
should_email(note.noteable.author_id)
should_email(note.noteable.assignee_id)
+ should_email(@u_mentioned.id)
should_not_email(note.author_id)
should_not_email(@u_participating.id)
should_not_email(@u_disabled.id)
@@ -235,9 +236,11 @@ describe NotificationService do
@u_watcher = create(:user, notification_level: Notification::N_WATCH)
@u_participating = create(:user, notification_level: Notification::N_PARTICIPATING)
@u_disabled = create(:user, notification_level: Notification::N_DISABLED)
+ @u_mentioned = create(:user, username: 'mention', notification_level: Notification::N_WATCH)
project.team << [@u_watcher, :master]
project.team << [@u_participating, :master]
project.team << [@u_disabled, :master]
+ project.team << [@u_mentioned, :master]
end
end