summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG19
-rw-r--r--CONTRIBUTING.md25
-rw-r--r--Gemfile16
-rw-r--r--Gemfile.lock45
-rw-r--r--README.md14
-rw-r--r--VERSION2
-rw-r--r--app/assets/javascripts/admin.js.coffee6
-rw-r--r--app/assets/javascripts/api.js.coffee2
-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/javascripts/users_select.js.coffee35
-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/assets/stylesheets/sections/stat_graph.scss19
-rw-r--r--app/assets/stylesheets/selects.scss4
-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.rb9
-rw-r--r--app/controllers/admin/projects_controller.rb9
-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.rb16
-rw-r--r--app/controllers/commit_controller.rb6
-rw-r--r--app/controllers/graphs_controller.rb (renamed from app/controllers/stat_graph_controller.rb)2
-rw-r--r--app/controllers/groups_controller.rb2
-rw-r--r--app/controllers/network_controller.rb (renamed from app/controllers/graph_controller.rb)2
-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/projects_controller.rb11
-rw-r--r--app/controllers/refs_controller.rb2
-rw-r--r--app/controllers/snippets_controller.rb8
-rw-r--r--app/controllers/teams_controller.rb2
-rw-r--r--app/helpers/application_helper.rb11
-rw-r--r--app/helpers/issues_helper.rb2
-rw-r--r--app/helpers/namespaces_helper.rb10
-rw-r--r--app/helpers/notes_helper.rb7
-rw-r--r--app/helpers/projects_helper.rb38
-rw-r--r--app/helpers/tree_helper.rb2
-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/namespace.rb1
-rw-r--r--app/models/note.rb2
-rw-r--r--app/models/project.rb23
-rw-r--r--app/models/user.rb22
-rw-r--r--app/observers/base_observer.rb4
-rw-r--r--app/observers/issue_observer.rb2
-rw-r--r--app/observers/merge_request_observer.rb2
-rw-r--r--app/observers/project_observer.rb16
-rw-r--r--app/observers/user_observer.rb11
-rw-r--r--app/services/notification_service.rb15
-rw-r--r--app/views/admin/dashboard/index.html.haml8
-rw-r--r--app/views/admin/groups/show.html.haml187
-rw-r--r--app/views/admin/hooks/_data_ex.html.erb29
-rw-r--r--app/views/admin/projects/index.html.haml4
-rw-r--r--app/views/admin/teams/index.html.haml65
-rw-r--r--app/views/admin/teams/members/new.html.haml54
-rw-r--r--app/views/admin/teams/projects/new.html.haml35
-rw-r--r--app/views/admin/teams/show.html.haml175
-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/graphs/show.html.haml (renamed from app/views/stat_graph/show.html.haml)4
-rw-r--r--app/views/graphs/show.js.haml (renamed from app/views/stat_graph/show.js.haml)0
-rw-r--r--app/views/groups/_new_group_member.html.haml2
-rw-r--r--app/views/groups/_new_member.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/_form.html.haml10
-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/network/_head.html.haml (renamed from app/views/graph/_head.html.haml)4
-rw-r--r--app/views/network/show.html.haml (renamed from app/views/graph/show.html.haml)2
-rw-r--r--app/views/network/show.json.erb (renamed from app/views/graph/show.json.erb)0
-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/_clone_panel.html.haml5
-rw-r--r--app/views/projects/_settings_nav.html.haml6
-rw-r--r--app/views/projects/fork.html.haml19
-rw-r--r--app/views/projects/show.html.haml1
-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/shared/_clone_panel.html.haml10
-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/database.yml.mysql2
-rw-r--r--config/database.yml.postgresql2
-rw-r--r--config/environments/production.rb2
-rw-r--r--config/gitlab.yml.example9
-rw-r--r--config/initializers/2_app.rb5
-rw-r--r--config/puma.rb.example7
-rw-r--r--config/routes.rb14
-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/20130611210815_increase_snippet_text_column_size.rb9
-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.md95
-rw-r--r--doc/make_release.md56
-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/admin/groups.feature1
-rw-r--r--features/group/group.feature1
-rw-r--r--features/project/issues/milestones.feature2
-rw-r--r--features/steps/admin/admin_groups.rb5
-rw-r--r--features/steps/admin/admin_teams.rb2
-rw-r--r--features/steps/group/group.rb3
-rw-r--r--features/steps/project/project_active_tab.rb8
-rw-r--r--features/steps/project/project_graph.rb2
-rw-r--r--features/steps/project/project_merge_requests.rb4
-rw-r--r--features/steps/project/project_milestones.rb8
-rw-r--r--features/steps/project/project_network_graph.rb2
-rw-r--r--features/steps/shared/paths.rb2
-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/issues.rb3
-rw-r--r--lib/api/merge_requests.rb3
-rw-r--r--lib/api/projects.rb36
-rw-r--r--lib/backup/repository.rb2
-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.rb9
-rw-r--r--lib/gitlab/inline_diff.rb7
-rw-r--r--lib/gitlab/version_info.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/backup.rake22
-rw-r--r--lib/tasks/gitlab/check.rake4
-rw-r--r--lib/tasks/gitlab/import.rake2
-rw-r--r--lib/tasks/gitlab/shell.rake10
-rwxr-xr-xscript/check2
-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/models/milestone_spec.rb1
-rw-r--r--spec/requests/api/projects_spec.rb67
-rw-r--r--spec/routing/project_routing_spec.rb18
-rw-r--r--spec/services/notification_service_spec.rb5
189 files changed, 2506 insertions, 1072 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 308e179b2b1..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
@@ -16,6 +26,15 @@ v 5.3.0
- Fix bug with team assignation on project from #4109
- Advanced snippets: public/private, project/personal (Andrew Kulakov)
- Repository Graphs (Karlo Nicholas T. Soriano)
+ - Fix dashboard lost if comment on commit
+ - Update gitlab-grack. Fixes issue with --depth option
+ - Fix project events duplicate on project page
+ - Fix postgres error when displaying network graph.
+ - Fix dashboard event filter when navigate via turbolinks
+ - 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 e63667e5673..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"
@@ -129,7 +137,7 @@ group :assets do
gem "modernizr", "2.6.2"
gem "raphael-rails", git: "https://github.com/gitlabhq/raphael-rails.git"
gem 'bootstrap-sass'
- gem "font-awesome-sass-rails", "~> 3.0.0"
+ gem "font-awesome-rails", "~> 3.1.1"
gem "gemoji", "~> 1.2.1", require: 'emoji/railtie'
gem "gon"
end
diff --git a/Gemfile.lock b/Gemfile.lock
index 91a08e1cf6b..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,9 +132,18 @@ GEM
eventmachine (>= 0.12.0)
ffaker (1.16.0)
ffi (1.8.1)
- font-awesome-sass-rails (3.0.2.2)
- railties (>= 3.1.1)
- sass-rails (>= 3.1.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)
dotenv (>= 0.7)
thor (>= 0.13.6)
@@ -169,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)
@@ -243,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)
@@ -266,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)
@@ -305,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)
@@ -354,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)
@@ -400,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)
@@ -499,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)
@@ -518,6 +539,7 @@ PLATFORMS
DEPENDENCIES
acts-as-taggable-on
annotate!
+ asciidoctor
awesome_print
better_errors
binding_of_caller
@@ -535,7 +557,8 @@ DEPENDENCIES
enumerize
factory_girl_rails
ffaker
- font-awesome-sass-rails (~> 3.0.0)
+ fog (~> 1.3.1)
+ font-awesome-rails (~> 3.1.1)
foreman
gemoji (~> 1.2.1)
github-linguist
@@ -545,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)
@@ -573,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)
@@ -585,6 +608,7 @@ DEPENDENCIES
redcarpet (~> 2.2.2)
redis-rails
rspec-rails
+ sanitize
sass-rails
sdoc
seed-fu
@@ -607,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 74bf3938eba..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)
@@ -115,11 +117,15 @@ or start each component separately
* [RSpec](http://rspec.info/) unit and functional tests
- bundle exec rake spec
+ All RSpec tests: bundle exec rake spec
+
+ Single RSpec file: bundle exec rspec spec/controllers/commit_controller_spec.rb
* [Spinach](https://github.com/codegram/spinach) integration tests
- bundle exec rake spinach
+ All Spinach tests: bundle exec rake spinach
+
+ Single Spinach test: bundle exec spinach features/project/issues/milestones.feature
### GitLab interfaces
@@ -139,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 9718a664113..c5e18371adf 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-5.3.0.pre
+5.4.0.pre
diff --git a/app/assets/javascripts/admin.js.coffee b/app/assets/javascripts/admin.js.coffee
index c83b74a76a2..da0077ea77b 100644
--- a/app/assets/javascripts/admin.js.coffee
+++ b/app/assets/javascripts/admin.js.coffee
@@ -19,11 +19,13 @@ class Admin
modal = $('.change-owner-holder')
- $('.change-owner-link').bind "click", ->
+ $('.change-owner-link').bind "click", (e) ->
+ e.preventDefault()
$(this).hide()
modal.show()
- $('.change-owner-cancel-link').bind "click", ->
+ $('.change-owner-cancel-link').bind "click", (e) ->
+ e.preventDefault()
modal.hide()
$('.change-owner-link').show()
diff --git a/app/assets/javascripts/api.js.coffee b/app/assets/javascripts/api.js.coffee
index 7cac971f247..db80e7b0f3c 100644
--- a/app/assets/javascripts/api.js.coffee
+++ b/app/assets/javascripts/api.js.coffee
@@ -50,5 +50,5 @@
callback(users)
buildUrl: (url) ->
- url = gon.relative_url_root + url if gon.relative_url_root.present?
+ url = gon.relative_url_root + url if gon.relative_url_root?
return url.replace(':version', gon.api_version)
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/javascripts/users_select.js.coffee b/app/assets/javascripts/users_select.js.coffee
index f9e523ea49f..8286ca2f0c1 100644
--- a/app/assets/javascripts/users_select.js.coffee
+++ b/app/assets/javascripts/users_select.js.coffee
@@ -14,23 +14,24 @@ $ ->
userFormatSelection = (user) ->
user.name
- $('.ajax-users-select').select2
- placeholder: "Search for a user"
- multiple: $('.ajax-users-select').hasClass('multiselect')
- minimumInputLength: 0
- query: (query) ->
- Api.users query.term, (users) ->
- data = { results: users }
- query.callback(data)
+ $('.ajax-users-select').each (i, select) ->
+ $(select).select2
+ placeholder: "Search for a user"
+ multiple: $(select).hasClass('multiselect')
+ minimumInputLength: 0
+ query: (query) ->
+ Api.users query.term, (users) ->
+ data = { results: users }
+ query.callback(data)
- initSelection: (element, callback) ->
- id = $(element).val()
- if id isnt ""
- Api.user(id, callback)
+ initSelection: (element, callback) ->
+ id = $(element).val()
+ if id isnt ""
+ Api.user(id, callback)
- formatResult: userFormatResult
- formatSelection: userFormatSelection
- dropdownCssClass: "ajax-users-dropdown"
- escapeMarkup: (m) -> # we do not want to escape markup since we are displaying html in results
- m
+ formatResult: userFormatResult
+ formatSelection: userFormatSelection
+ dropdownCssClass: "ajax-users-dropdown"
+ escapeMarkup: (m) -> # we do not want to escape markup since we are displaying html in results
+ m
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/assets/stylesheets/sections/stat_graph.scss b/app/assets/stylesheets/sections/stat_graph.scss
index a0bf8568a75..4baec343d6e 100644
--- a/app/assets/stylesheets/sections/stat_graph.scss
+++ b/app/assets/stylesheets/sections/stat_graph.scss
@@ -1,5 +1,4 @@
.tint-box {
- border-radius: 6px;
background: #f3f3f3;
position: relative;
margin-bottom: 10px;
@@ -16,13 +15,11 @@
}
#contributors .person {
- -moz-box-sizing: border-box;
- box-sizing: border-box;
+ &:nth-child(even) {
+ float: right;
+ }
float: left;
- border-radius: 2px;
- margin: 3px;
- padding: 7px;
- border: 1px solid #ddd;
+ margin-top: 10px;
}
.contributors-list {
@@ -33,7 +30,7 @@
#contributors .person .spark {
display: block;
- background: #f7f7f7;
+ background: #f3f3f3;
}
#contributors .person .area-contributor {
@@ -49,9 +46,3 @@
shape-rendering: crispedges;
stroke-dasharray: 3 3;
}
-
-.right{
- float: right;
- display: inline-block;
- margin-top: 5px;
-}
diff --git a/app/assets/stylesheets/selects.scss b/app/assets/stylesheets/selects.scss
index 7abbe80bd39..9b7b6ad583c 100644
--- a/app/assets/stylesheets/selects.scss
+++ b/app/assets/stylesheets/selects.scss
@@ -10,6 +10,10 @@
.ajax-users-select {
width: 400px;
+
+ &.input-large {
+ width: 210px;
+ }
}
.user-result {
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 df520bea773..749c8fbecef 100644
--- a/app/controllers/admin/groups_controller.rb
+++ b/app/controllers/admin/groups_controller.rb
@@ -8,12 +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?)
-
- @users = User.active
end
def new
@@ -68,7 +62,8 @@ class Admin::GroupsController < Admin::ApplicationController
end
def project_teams_update
- @group.add_users_to_project_teams(params[:user_ids], params[:project_access])
+ @group.add_users_to_project_teams(params[:user_ids].split(','), params[:project_access])
+
redirect_to [:admin, @group], notice: 'Users were successfully added.'
end
diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb
index bbb80cbb839..a63c4694a81 100644
--- a/app/controllers/admin/projects_controller.rb
+++ b/app/controllers/admin/projects_controller.rb
@@ -2,8 +2,10 @@ class Admin::ProjectsController < Admin::ApplicationController
before_filter :project, only: [:edit, :show, :update, :destroy, :team_update]
def index
- @projects = Project.scoped
- @projects = @projects.where(namespace_id: params[:namespace_id]) if params[:namespace_id].present?
+ owner_id = params[:owner_id]
+ user = User.find_by_id(owner_id)
+
+ @projects = user ? user.owned_projects : Project.scoped
@projects = @projects.where(public: true) if params[:public_only].present?
@projects = @projects.with_push if params[:with_push].present?
@projects = @projects.abandoned if params[:abandoned].present?
@@ -14,9 +16,6 @@ class Admin::ProjectsController < Admin::ApplicationController
def show
@repository = @project.repository
- @users = User.active
- @users = @users.not_in_project(@project) if @project.users.present?
- @users = @users.all
end
protected
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 9bb86b80d1e..fda05feefc0 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -1,7 +1,8 @@
class ApplicationController < ActionController::Base
before_filter :authenticate_user!
before_filter :reject_blocked!
- before_filter :set_current_user_for_observers
+ before_filter :check_password_expiration
+ before_filter :set_current_user_for_thread
before_filter :add_abilities
before_filter :dev_tools if Rails.env == 'development'
before_filter :default_headers
@@ -47,9 +48,8 @@ class ApplicationController < ActionController::Base
end
end
- def set_current_user_for_observers
- MergeRequestObserver.current_user = current_user
- IssueObserver.current_user = current_user
+ def set_current_user_for_thread
+ Thread.current[:current_user] = current_user
end
def abilities
@@ -154,7 +154,13 @@ class ApplicationController < ActionController::Base
gon.default_issues_tracker = Project.issues_tracker.default_value
gon.api_version = API::API.version
gon.api_token = current_user.private_token if current_user
- gon.gravatar_url = request.ssl? ? Gitlab.config.gravatar.ssl_url : Gitlab.config.gravatar.plain_url
+ 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/commit_controller.rb b/app/controllers/commit_controller.rb
index 1329410891d..a164de33107 100644
--- a/app/controllers/commit_controller.rb
+++ b/app/controllers/commit_controller.rb
@@ -11,7 +11,11 @@ class CommitController < ProjectResourceController
result = CommitLoadContext.new(project, current_user, params).execute
@commit = result[:commit]
- git_not_found! unless @commit
+
+ if @commit.nil?
+ git_not_found!
+ return
+ end
@suppress_diff = result[:suppress_diff]
diff --git a/app/controllers/stat_graph_controller.rb b/app/controllers/graphs_controller.rb
index f076f4726d0..5ae9c15c0f7 100644
--- a/app/controllers/stat_graph_controller.rb
+++ b/app/controllers/graphs_controller.rb
@@ -1,4 +1,4 @@
-class StatGraphController < ProjectResourceController
+class GraphsController < ProjectResourceController
# Authorize
before_filter :authorize_read_project!
before_filter :authorize_code_access!
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index efc93329ff5..e6559b8d8fe 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -74,7 +74,7 @@ class GroupsController < ApplicationController
end
def team_members
- @group.add_users_to_project_teams(params[:user_ids], params[:project_access])
+ @group.add_users_to_project_teams(params[:user_ids].split(','), params[:project_access])
redirect_to people_group_path(@group), notice: 'Users were successfully added.'
end
diff --git a/app/controllers/graph_controller.rb b/app/controllers/network_controller.rb
index c79ed5ca3cc..3c8e747ba4e 100644
--- a/app/controllers/graph_controller.rb
+++ b/app/controllers/network_controller.rb
@@ -1,4 +1,4 @@
-class GraphController < ProjectResourceController
+class NetworkController < ProjectResourceController
include ExtractsPath
include ApplicationHelper
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/projects_controller.rb b/app/controllers/projects_controller.rb
index f2dbc519797..fad681eeef8 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -7,7 +7,7 @@ class ProjectsController < ProjectResourceController
before_filter :authorize_admin_project!, only: [:edit, :update, :destroy, :transfer]
before_filter :require_non_empty_project, only: [:blob, :tree, :graph]
- layout 'navless', only: [:new, :create]
+ layout 'navless', only: [:new, :create, :fork]
before_filter :set_title, only: [:new, :create]
def new
@@ -81,14 +81,15 @@ class ProjectsController < ProjectResourceController
end
def fork
- @project = ::Projects::ForkContext.new(project, current_user).execute
+ @forked_project = ::Projects::ForkContext.new(project, current_user).execute
respond_to do |format|
format.html do
- if @project.saved? && @project.forked?
- redirect_to(@project, notice: 'Project was successfully forked.')
+ if @forked_project.saved? && @forked_project.forked?
+ redirect_to(@forked_project, notice: 'Project was successfully forked.')
else
- render action: "new"
+ @title = 'Fork project'
+ render action: "fork"
end
end
format.js
diff --git a/app/controllers/refs_controller.rb b/app/controllers/refs_controller.rb
index e7def3984f8..cae9193a1da 100644
--- a/app/controllers/refs_controller.rb
+++ b/app/controllers/refs_controller.rb
@@ -14,7 +14,7 @@ class RefsController < ProjectResourceController
elsif params[:destination] == "blob"
project_blob_path(@project, (@id))
elsif params[:destination] == "graph"
- project_graph_path(@project, @id, @options)
+ project_network_path(@project, @id, @options)
else
project_commits_path(@project, @id)
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/controllers/teams_controller.rb b/app/controllers/teams_controller.rb
index 73c2fb43326..57ab2a88e03 100644
--- a/app/controllers/teams_controller.rb
+++ b/app/controllers/teams_controller.rb
@@ -17,7 +17,7 @@ class TeamsController < ApplicationController
def edit
projects
- @avaliable_projects = current_user.admin? ? Project.without_team(user_team) : current_user.owned_projects.without_team(user_team)
+ @avaliable_projects = current_user.owned_projects.without_team(user_team)
end
def update
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 935fb327c62..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
@@ -192,9 +192,12 @@ module ApplicationHelper
alias_method :url_to_image, :image_url
def users_select_tag(id, opts = {})
- css_class = "ajax-users-select"
- css_class << " multiselect" if opts[:multiple]
- hidden_field_tag(id, '', class: css_class)
+ css_class = "ajax-users-select "
+ css_class << "multiselect " if opts[:multiple]
+ css_class << (opts[:class] || '')
+ value = opts[:selected] || ''
+
+ hidden_field_tag(id, value, class: css_class)
end
def body_data_page
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/namespaces_helper.rb b/app/helpers/namespaces_helper.rb
index a9a6c78654f..69ad3de5031 100644
--- a/app/helpers/namespaces_helper.rb
+++ b/app/helpers/namespaces_helper.rb
@@ -1,13 +1,7 @@
module NamespacesHelper
def namespaces_options(selected = :current_user, scope = :default)
- if current_user.admin
- groups = Group.all
- users = Namespace.root
- else
- groups = current_user.owned_groups.select {|n| n.type == 'Group'}
- users = current_user.namespaces.reject {|n| n.type == 'Group'}
- end
-
+ groups = current_user.owned_groups.select {|n| n.type == 'Group'}
+ users = current_user.namespaces.reject {|n| n.type == 'Group'}
global_opts = ["Global", [['/', Namespace.global_id]] ]
group_opts = ["Groups", groups.sort_by(&:human_name).map {|g| [g.human_name, g.id]} ]
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/helpers/tree_helper.rb b/app/helpers/tree_helper.rb
index a8491dfe3ba..af633f6fd04 100644
--- a/app/helpers/tree_helper.rb
+++ b/app/helpers/tree_helper.rb
@@ -48,7 +48,7 @@ module TreeHelper
end
def plain_text_readme? filename
- filename == 'README'
+ filename =~ /^README(.txt)?$/i
end
# Simple shortcut to File.join
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/namespace.rb b/app/models/namespace.rb
index cb7164eab13..c74e0cf5a1d 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -27,6 +27,7 @@ class Namespace < ActiveRecord::Base
message: "only letters, digits, spaces & '_' '-' '.' allowed." }
validates :description, length: { within: 0..255 }
validates :path, uniqueness: true, presence: true, length: { within: 1..255 },
+ exclusion: { in: Gitlab::Blacklist.path },
format: { with: Gitlab::Regex.path_regex,
message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
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 8cb290f6601..e3f88b2cf98 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -79,6 +79,7 @@ class Project < ActiveRecord::Base
format: { with: Gitlab::Regex.project_name_regex,
message: "only letters, digits, spaces & '_' '-' '.' allowed. Letter should be first" }
validates :path, presence: true, length: { within: 0..255 },
+ exclusion: { in: Gitlab::Blacklist.path },
format: { with: Gitlab::Regex.path_regex,
message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
validates :issues_enabled, :wall_enabled, :merge_requests_enabled,
@@ -89,10 +90,10 @@ 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, :repo_name
+ validate :check_limit
# Scopes
scope :without_user, ->(user) { where("projects.id NOT IN (:ids)", ids: user.authorized_projects.map(&:id) ) }
@@ -110,11 +111,7 @@ class Project < ActiveRecord::Base
class << self
def abandoned
- project_ids = Event.select('max(created_at) as latest_date, project_id').
- group('project_id').
- having('latest_date < ?', 6.months.ago).map(&:project_id)
-
- where(id: project_ids)
+ where('projects.last_activity_at < ?', 6.months.ago)
end
def with_push
@@ -170,14 +167,6 @@ class Project < ActiveRecord::Base
errors[:base] << ("Can't check your ability to create project")
end
- def repo_name
- denied_paths = %w(admin dashboard groups help profile projects search)
-
- if denied_paths.include?(path)
- errors.add(:path, "like #{path} is not allowed")
- end
- end
-
def to_param
if namespace
namespace.path + "/" + path
@@ -424,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 3f51d7a9938..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
@@ -104,6 +107,7 @@ class User < ActiveRecord::Base
validates :extern_uid, allow_blank: true, uniqueness: {scope: :provider}
validates :projects_limit, presence: true, numericality: {greater_than_or_equal_to: 0}
validates :username, presence: true, uniqueness: true,
+ exclusion: { in: Gitlab::Blacklist.path },
format: { with: Gitlab::Regex.username_regex,
message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
@@ -112,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
@@ -363,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/base_observer.rb b/app/observers/base_observer.rb
index 182d3b7b73c..92b73981d27 100644
--- a/app/observers/base_observer.rb
+++ b/app/observers/base_observer.rb
@@ -6,4 +6,8 @@ class BaseObserver < ActiveRecord::Observer
def log_info message
Gitlab::AppLogger.info message
end
+
+ def current_user
+ Thread.current[:current_user]
+ end
end
diff --git a/app/observers/issue_observer.rb b/app/observers/issue_observer.rb
index 03ce4b95ac8..888fa7f6b73 100644
--- a/app/observers/issue_observer.rb
+++ b/app/observers/issue_observer.rb
@@ -1,6 +1,4 @@
class IssueObserver < BaseObserver
- cattr_accessor :current_user
-
def after_create(issue)
notification.new_issue(issue, current_user)
end
diff --git a/app/observers/merge_request_observer.rb b/app/observers/merge_request_observer.rb
index d0dfad8869d..03d4a22c1e6 100644
--- a/app/observers/merge_request_observer.rb
+++ b/app/observers/merge_request_observer.rb
@@ -1,6 +1,4 @@
class MergeRequestObserver < BaseObserver
- cattr_accessor :current_user
-
def after_create(merge_request)
notification.new_merge_request(merge_request, current_user)
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/observers/user_observer.rb b/app/observers/user_observer.rb
index 6bb3c471d0c..e969405a598 100644
--- a/app/observers/user_observer.rb
+++ b/app/observers/user_observer.rb
@@ -10,12 +10,11 @@ class UserObserver < BaseObserver
end
def after_save user
- if user.username_changed?
- if user.namespace
- user.namespace.update_attributes(path: user.username)
- else
- user.create_namespace!(path: user.username, name: user.username)
- end
+ # Ensure user has namespace
+ user.create_namespace!(path: user.username, name: user.username) unless user.namespace
+
+ if user.username_changed? || user.name_changed?
+ user.namespace.update_attributes(path: user.username, name: user.name)
end
end
end
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/dashboard/index.html.haml b/app/views/admin/dashboard/index.html.haml
index d3c938bb8f2..a69b26073f0 100644
--- a/app/views/admin/dashboard/index.html.haml
+++ b/app/views/admin/dashboard/index.html.haml
@@ -50,6 +50,14 @@
%h4 Stats
%hr
%p
+ Teams
+ %span.light.pull-right
+ = UserTeam.count
+ %p
+ Forks
+ %span.light.pull-right
+ = ForkedProjectLink.count
+ %p
Issues
%span.light.pull-right
= Issue.count
diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml
index 0e2e144d326..9c4b91b1bfa 100644
--- a/app/views/admin/groups/show.html.haml
+++ b/app/views/admin/groups/show.html.haml
@@ -1,120 +1,93 @@
%h3.page_title
Group: #{@group.name}
-%br
-%table.zebra-striped
- %thead
- %tr
- %th Group
- %th
- %tr
- %td
- %b
- Name:
- %td
- = @group.name
- &nbsp;
- = link_to edit_admin_group_path(@group), class: "btn btn-small pull-right" do
- %i.icon-edit
- Edit
- %tr
- %td
- %b
- Description:
- %td
- = @group.description
- %tr
- %td
- %b
- Path:
- %td
- %span.monospace= File.join(Gitlab.config.gitlab_shell.repos_path, @group.path)
- %tr
- %td
- %b
- Owner:
- %td
- = @group.owner_name
- .pull-right
- = link_to "#", class: "btn btn-small change-owner-link" do
- %i.icon-edit
- Change owner
+ = link_to edit_admin_group_path(@group), class: "btn btn-small pull-right" do
+ %i.icon-edit
+ Edit
+%hr
+.row
+ .span6
+ .ui-box
+ %h5.title
+ Group info:
+ %ul.well-list
+ %li
+ %span.light Name:
+ %strong= @group.name
+ %li
+ %span.light Path:
+ %strong
+ = @group.path
- %tr.change-owner-holder.hide
- %td.bgred
- %b.cred
- New Owner:
- %td.bgred
- = form_for [:admin, @group] do |f|
- = f.select :owner_id, User.all.map { |user| [user.name, user.id] }, {}, {class: 'chosen'}
- %div
- = f.submit 'Change Owner', class: "btn btn-remove"
- = link_to "Cancel", "#", class: "btn change-owner-cancel-link"
+ %li
+ %span.light Description:
+ %strong
+ = @group.description
-- if @group.projects.any?
- %fieldset
- %legend Projects (#{@group.projects.count})
- %table
- %thead
- %tr
- %th Project name
- %th Path
- %th Users
- %th.cred Danger Zone!
- - @group.projects.each do |project|
- %tr
- %td
- = link_to project.name_with_namespace, [:admin, project]
- %td
- %span.monospace= project.path_with_namespace + ".git"
- %td= project.users.count
- %td.bgred
- = link_to 'Transfer project to global namespace', remove_project_admin_group_path(@group, project_id: project.id), confirm: 'Remove project from group and move to global namespace. Are you sure?', method: :delete, class: "btn btn-remove small"
+ %li
+ %span.light Owned by:
+ %strong
+ - if @group.owner
+ = link_to @group.owner_name, admin_user_path(@group.owner)
+ - else
+ (deleted)
+ .pull-right
+ = link_to "#", class: "btn btn-small change-owner-link" do
+ %i.icon-edit
+ Change owner
+ %li.change-owner-holder.hide.bgred
+ .form-holder
+ %strong.cred New Owner:
+ = form_for [:admin, @group] do |f|
+ = users_select_tag(:"group[owner_id]")
+ .prepend-top-10
+ = f.submit 'Change Owner', class: "btn btn-remove"
+ = link_to "Cancel", "#", class: "btn change-owner-cancel-link"
- = form_tag project_teams_update_admin_group_path(@group), id: "new_team_member", class: "bulk_import", method: :put do
- %table.zebra-striped
- %thead
- %tr
- %th Users
- %th Project Access:
+ %li
+ %span.light Created at:
+ %strong
+ = @group.created_at.stamp("March 1, 1999")
- - @group.users.each do |user|
- - next unless user
- %tr{class: "user_#{user.id}"}
- %td.name= link_to user.name, admin_user_path(user)
- %td.projects_access
- - user.authorized_projects.in_namespace(@group).each do |project|
- - u_p = user.users_projects.in_project(project).first
- - next unless u_p
- %span
- = project.name_with_namespace
- = link_to "(#{ u_p.project_access_human })", edit_admin_project_member_path(project, user)
- %tr
- %td.input= select_tag :user_ids, options_from_collection_for_select(@users , :id, :name), multiple: true, data: {placeholder: 'Select users'}, class: 'chosen span5'
- %td= select_tag :project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3"}
- %tr
- %td= submit_tag 'Add user to projects in group', class: "btn btn-create"
- %td
+ .ui-box
+ %h5.title
+ Add user to Group projects:
+ .ui-box-body.form-holder
+ %p.light
Read more about project permissions
%strong= link_to "here", help_permissions_path, class: "vlink"
-- else
- %fieldset
- %legend Group is empty
+ = form_tag project_teams_update_admin_group_path(@group), id: "new_team_member", class: "bulk_import", method: :put do
+ %div
+ = users_select_tag(:user_ids, multiple: true)
+ %div.prepend-top-10
+ = select_tag :project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span2"}
+ %hr
+ = submit_tag 'Add user to projects in group', class: "btn btn-create"
+ .ui-box
+ %h5.title
+ Users from Group projects
+ %small
+ (#{@group.users.count})
+ %ul.well-list
+ - @group.users.sort_by(&:name).each do |user|
+ %li{class: dom_class(user)}
+ %strong
+ = link_to user.name, admin_user_path(user)
+ %span.pull-right.light
+ = pluralize user.authorized_projects.in_namespace(@group).count, 'project'
-= form_tag project_update_admin_group_path(@group), class: "bulk_import", method: :put do
- %fieldset
- %legend Move projects to group
- .alert
- You can move only projects with existing repos
- %br
- Group projects will be moved in group directory and will not be accessible by old path
- .clearfix
- = label_tag :project_ids do
+ .span6
+ .ui-box
+ %h5.title
Projects
- .input
- = select_tag :project_ids, options_from_collection_for_select(@projects , :id, :name_with_namespace), multiple: true, data: {placeholder: 'Select projects'}, class: 'chosen span5'
- .form-actions
- = submit_tag 'Move projects', class: "btn btn-create"
-
+ %small
+ (#{@group.projects.count})
+ %ul.well-list
+ - @group.projects.sort_by(&:name).each do |project|
+ %li
+ %strong
+ = link_to project.name_with_namespace, [:admin, project]
+ %span.pull-right.light
+ %span.monospace= project.path_with_namespace + ".git"
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/projects/index.html.haml b/app/views/admin/projects/index.html.haml
index af65a868b5c..70ea460a06f 100644
--- a/app/views/admin/projects/index.html.haml
+++ b/app/views/admin/projects/index.html.haml
@@ -14,9 +14,9 @@
= text_field_tag :name, params[:name], class: "span2"
.control-group
- = label_tag :namespace_id, 'Namespace:', class: 'control-label'
+ = label_tag :owner_id, 'Owner:', class: 'control-label'
.controls
- = select_tag :namespace_id, namespaces_options(params[:namespace_id], :all), class: "chosen span2", prompt: "Any"
+ = users_select_tag :owner_id, selected: params[:owner_id], class: 'input-large'
.control-group
= label_tag :public_only, 'Public Only', class: 'control-label'
.controls
diff --git a/app/views/admin/teams/index.html.haml b/app/views/admin/teams/index.html.haml
index cf24ed5398f..05ddc3cee89 100644
--- a/app/views/admin/teams/index.html.haml
+++ b/app/views/admin/teams/index.html.haml
@@ -1,43 +1,48 @@
%h3.page_title
- Teams
+ Teams (#{@teams.total_count})
%small
allow you to organize groups of people that have a common focus. Use teams to simplify the process of assigning roles to groups of people.
= link_to 'New Team', new_admin_team_path, class: "btn btn-small pull-right"
- %br
+%br
= form_tag admin_teams_path, method: :get, class: 'form-inline' do
- = text_field_tag :name, params[:name], class: "xlarge"
+ = text_field_tag :name, params[:name], class: "span6"
= submit_tag "Search", class: "btn submit btn-primary"
-%table
- %thead
- %tr
- %th
- Name
- %i.icon-sort-down
- %th Description
- %th Path
- %th Projects
- %th Members
- %th Owner
- %th.cred Danger Zone!
+%hr
+%ul.bordered-list
- @teams.each do |team|
- %tr
- %td
- %strong= link_to team.name, admin_team_path(team)
- %td= truncate team.description
- %td= team.path
- %td= team.projects.count
- %td= team.members.count
- %td
- - if team.owner
- = link_to team.owner.name, admin_user_path(team.owner)
- - else
- (deleted)
- %td.bgred
- = link_to 'Edit', edit_admin_team_path(team), id: "edit_#{dom_id(team)}", class: "btn btn-small"
- = link_to 'Destroy', admin_team_path(team), confirm: "REMOVE #{team.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove"
+ %li
+ .clearfix
+ .pull-right.prepend-top-10
+ = link_to 'Edit', edit_admin_team_path(team), id: "edit_#{dom_id(team)}", class: "btn btn-small"
+ = link_to 'Destroy', admin_team_path(team), confirm: "REMOVE #{team.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove"
+
+ %h4
+ = link_to admin_team_path(team) do
+ %i.icon-group
+ = team.name
+
+ .clearfix.light.append-bottom-10
+ %span
+ %b Owner:
+ - if team.owner
+ = link_to team.owner.name, admin_user_path(team.owner)
+ - else
+ (deleted)
+ \|
+ %span
+ %b Users:
+ %span.badge= team.members.count
+ \|
+ %span
+ %b Projects:
+ %span.badge= team.projects.count
+
+ .clearfix
+ %p
+ = truncate team.description, length: 150
= paginate @teams, theme: "gitlab"
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 b60dad35214..21bf65f9c3d 100644
--- a/app/views/admin/teams/projects/new.html.haml
+++ b/app/views/admin/teams/projects/new.html.haml
@@ -1,23 +1,18 @@
%h3.page_title
Team: #{@team.name}
+%hr
+= form_tag admin_team_projects_path(@team), id: "assign_projects", class: "bulk_import", method: :post do
+ %h6 Choose Projects you want to assign:
+ .clearfix
+ = label_tag :project_ids, "Projects"
+ .input
+ = select_tag :project_ids, options_from_collection_for_select(@projects , :id, :name_with_namespace), multiple: true, data: {placeholder: 'Select projects'}, class: 'chosen span5'
-%fieldset
- %legend Projects (#{@team.projects.count})
- = form_tag admin_team_projects_path(@team), id: "assign_projects", class: "bulk_import", method: :post do
- %table#projects_list
- %thead
- %tr
- %th Project name
- %th Max access
- %th
- - @team.projects.each do |project|
- %tr.project
- %td
- = link_to project.name_with_namespace, [:admin, project]
- %td
- %span= @team.human_max_project_access(project)
- %td
- %tr
- %td= select_tag :project_ids, options_from_collection_for_select(@projects , :id, :name_with_namespace), multiple: true, data: {placeholder: 'Select projects'}, class: 'chosen span5'
- %td= select_tag :greatest_project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3" }
- %td= submit_tag 'Add', class: "btn btn-primary", id: :assign_projects_to_team
+ %h6 Choose greatest user access for your team in these projects:
+ .clearfix
+ = label_tag :greatest_project_access, "Greatest Access"
+ .input
+ = select_tag :greatest_project_access, options_for_select(Project.access_options), {class: "project-access-select chosen span3" }
+
+ .form-actions
+ = submit_tag 'Add team to projects', class: "btn btn-create", id: :assign_projects_to_team
diff --git a/app/views/admin/teams/show.html.haml b/app/views/admin/teams/show.html.haml
index bd4d90b607f..cf105c096e3 100644
--- a/app/views/admin/teams/show.html.haml
+++ b/app/views/admin/teams/show.html.haml
@@ -1,93 +1,96 @@
%h3.page_title
Team: #{@team.name}
-%br
-%table.zebra-striped
- %thead
- %tr
- %th Team
- %th
- %tr
- %td
- %b
- Name:
- %td
- = @team.name
- &nbsp;
- = link_to edit_admin_team_path(@team), class: "btn btn-small pull-right" do
- %i.icon-edit
- Edit
- %tr
- %td
- %b
- Description:
- %td
- = @team.description
- %tr
- %td
- %b
- Owner:
- %td
- = @team.owner.name
- .pull-right
- = link_to "#", class: "btn btn-small change-owner-link" do
- %i.icon-edit
- Change owner
- %tr.change-owner-holder.hide
- %td.bgred
- %b.cred
- New Owner:
- %td.bgred
- = form_for @team, url: admin_team_path(@team) do |f|
- = f.select :owner_id, User.all.map { |user| [user.name, user.id] }, {}, {class: 'chosen'}
- %div
- = f.submit 'Change Owner', class: "btn btn-remove"
- = link_to "Cancel", "#", class: "btn change-owner-cancel-link"
+ = link_to edit_admin_team_path(@team), class: "btn btn-small pull-right" do
+ %i.icon-edit
+ Edit
+%hr
-%fieldset
- %legend
- Members (#{@team.members.count})
- %span= link_to 'Add members', new_admin_team_member_path(@team), class: "btn btn-primary btn-small pull-right", id: :add_members_to_team
- - if @team.members.any?
- %table#members_list
- %thead
- %tr
- %th User name
- %th Default project access
- %th Team access
- %th.cred.span3 Danger Zone!
- - @team.members.each do |member|
- %tr.member{ class: "user_#{member.id}"}
- %td
+
+.row
+ .span6
+ .ui-box
+ %h5.title
+ Team info:
+ %ul.well-list
+ %li
+ %span.light Name:
+ %strong= @team.name
+ %li
+ %span.light Path:
+ %strong
+ = @team.path
+
+ %li
+ %span.light Description:
+ %strong
+ = @team.description
+
+ %li
+ %span.light Owned by:
+ %strong
+ - if @team.owner
+ = link_to @team.owner.name, admin_user_path(@team.owner)
+ - else
+ (deleted)
+ .pull-right
+ = link_to "#", class: "btn btn-small change-owner-link" do
+ %i.icon-edit
+ Change owner
+ %li.change-owner-holder.hide.bgred
+ .form-holder
+ %strong.cred New Owner:
+ = form_for @team, url: admin_team_path(@team) do |f|
+ = users_select_tag(:"user_team[owner_id]")
+ .prepend-top-10
+ = f.submit 'Change Owner', class: "btn btn-remove"
+ = link_to "Cancel", "#", class: "btn change-owner-cancel-link"
+
+ %li
+ %span.light Created at:
+ %strong
+ = @team.created_at.stamp("March 1, 1999")
+
+ .span6
+ .ui-box
+ %h5.title
+ Members (#{@team.members.count})
+ .pull-right
+ = link_to 'Add members', new_admin_team_member_path(@team), class: "btn btn-small", id: :add_members_to_team
+ %ul.well-list#members_list
+ - @team.members.each do |member|
+ %li.member{ class: "user_#{member.id}"}
= link_to [:admin, member] do
- = member.name
- %small= "(#{member.email})"
- %td= @team.human_default_projects_access(member)
- %td= @team.admin?(member) ? "Admin" : "Member"
- %td.bgred
- = link_to 'Edit', edit_admin_team_member_path(@team, member), class: "btn btn-small"
- &nbsp;
- = link_to 'Remove', admin_team_member_path(@team, member), confirm: 'Remove member from team. Are you sure?', method: :delete, class: "btn btn-remove btn-small", id: "remove_member_#{member.id}"
+ %strong
+ = member.name
+ .pull-right
+ %span.light
+ = @team.human_default_projects_access(member)
+ - if @team.admin?(member)
+ %span.label.label-info Admin
+ &nbsp;
+ = link_to 'Edit', edit_admin_team_member_path(@team, member), class: "btn btn-small"
+ &nbsp;
+ = link_to 'Remove', admin_team_member_path(@team, member), confirm: 'Remove member from team. Are you sure?', method: :delete, class: "btn btn-remove btn-small", id: "remove_member_#{member.id}"
+
+
+ .ui-box
+ %h5.title
+ Projects (#{@team.projects.count})
+ .pull-right
+ = link_to 'Add projects', new_admin_team_project_path(@team), class: "btn btn-small", id: :assign_projects_to_team
+ %ul.well-list#projects_list
+ - @team.projects.each do |project|
+ %li.project
+ = link_to [:admin, project] do
+ %strong
+ = project.name_with_namespace
-%fieldset
- %legend
- Projects (#{@team.projects.count})
- %span= link_to 'Add projects', new_admin_team_project_path(@team), class: "btn btn-primary btn-small pull-right", id: :assign_projects_to_team
- - if @team.projects.any?
- %table#projects_list
- %thead
- %tr
- %th Project name
- %th Max access
- %th.cred.span3 Danger Zone!
- - @team.projects.each do |project|
- %tr.project
- %td
- = link_to project.name_with_namespace, [:admin, project]
- %td
- %span= @team.human_max_project_access(project)
- %td.bgred
- = link_to 'Edit', edit_admin_team_project_path(@team, project), class: "btn btn-small"
- &nbsp;
- = link_to 'Relegate', admin_team_project_path(@team, project), confirm: 'Remove project from team. Are you sure?', method: :delete, class: "btn btn-remove small", id: "relegate_project_#{project.id}"
+ .pull-right
+ %span.light
+ = @team.human_max_project_access(project)
+ &nbsp;
+ = link_to 'Edit', edit_admin_team_project_path(@team, project), class: "btn btn-small"
+ &nbsp;
+ = link_to 'Relegate', admin_team_project_path(@team, project), confirm: 'Remove project from team. Are you sure?', method: :delete, class: "btn btn-remove small", id: "relegate_project_#{project.id}"
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/stat_graph/show.html.haml b/app/views/graphs/show.html.haml
index 21af1153fcf..05bc1436e6d 100644
--- a/app/views/stat_graph/show.html.haml
+++ b/app/views/graphs/show.html.haml
@@ -5,12 +5,12 @@
.stat-graph
.header.clearfix
- .right
+ .pull-right
%select
%option{:value => "commits"} Commits
%option{:value => "additions"} Additions
%option{:value => "deletions"} Deletions
- %h3#date_header
+ %h3#date_header.page_title
%input#brush_change{:type => "hidden"}
.graphs
#contributors-master
diff --git a/app/views/stat_graph/show.js.haml b/app/views/graphs/show.js.haml
index b7c9b4113e9..b7c9b4113e9 100644
--- a/app/views/stat_graph/show.js.haml
+++ b/app/views/graphs/show.js.haml
diff --git a/app/views/groups/_new_group_member.html.haml b/app/views/groups/_new_group_member.html.haml
index 9cdbea60370..78db1c23320 100644
--- a/app/views/groups/_new_group_member.html.haml
+++ b/app/views/groups/_new_group_member.html.haml
@@ -5,7 +5,7 @@
%h6 1. Choose people you want in the team
.clearfix
= f.label :user_ids, "People"
- .input= select_tag(:user_ids, options_from_collection_for_select(User.active.alphabetically, :id, :name), {data: {placeholder: "Select users"}, class: "chosen xxlarge", multiple: true})
+ .input= users_select_tag(:user_ids, multiple: true)
%h6 2. Set access level for them
.clearfix
diff --git a/app/views/groups/_new_member.html.haml b/app/views/groups/_new_member.html.haml
index b3424b01bcb..4a762ed39ed 100644
--- a/app/views/groups/_new_member.html.haml
+++ b/app/views/groups/_new_member.html.haml
@@ -5,7 +5,7 @@
%h6 1. Choose people you want in the team
.clearfix
= f.label :user_ids, "People"
- .input= select_tag(:user_ids, options_from_collection_for_select(User.not_in_project(@project).alphabetically, :id, :name), {data: {placeholder: "Select users"}, class: "chosen xxlarge", multiple: true})
+ .input= users_select_tag(:user_ids, multiple: true)
%h6 2. Set access level for them
.clearfix
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/_form.html.haml b/app/views/issues/_form.html.haml
index 2bb5e6ca5ac..57460e623ab 100644
--- a/app/views/issues/_form.html.haml
+++ b/app/views/issues/_form.html.haml
@@ -20,8 +20,11 @@
%i.icon-user
Assign to
.input
- = f.select(:assignee_id, @project.users.alphabetically.collect {|p| [ p.name, p.id ] }, { include_blank: "Select a user" }, {class: 'chosen'})
- = link_to 'Assign to me', '#', class: 'btn btn-small assign-to-me-link'
+ .pull-left
+ = f.select(:assignee_id, @project.users.alphabetically.collect {|p| [ p.name, p.id ] }, { include_blank: "Select a user" }, {class: 'chosen'})
+ .pull-right
+ &nbsp;
+ = link_to 'Assign to me', '#', class: 'btn btn-small assign-to-me-link'
.issue_milestone.pull-left
= f.label :milestone_id do
%i.icon-time
@@ -64,6 +67,9 @@
event.preventDefault();
}
})
+ .bind( "click", function( event ) {
+ $( this ).autocomplete("search", "");
+ })
.autocomplete({
minLength: 0,
source: function( request, response ) {
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 399bcf5de2e..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(graph)) do
- = link_to "Network", project_graph_path(@project, @ref || @repository.root_ref)
- = nav_link(controller: %w(stat_graph)) do
- = link_to "Graphs", project_stat_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/graph/_head.html.haml b/app/views/network/_head.html.haml
index 7a5b3c6f43d..62ab8b049ac 100644
--- a/app/views/graph/_head.html.haml
+++ b/app/views/network/_head.html.haml
@@ -5,7 +5,7 @@
.pull-left
= render partial: 'shared/ref_switcher', locals: {destination: 'graph'}
.pull-left
- = form_tag project_graph_path(@project, @id), method: :get do |f|
+ = form_tag project_network_path(@project, @id), method: :get do |f|
.control-group
= label_tag :filter_ref, "Show only selected ref", class: 'control-label light'
.controls
@@ -14,7 +14,7 @@
= hidden_field_tag(key, value, id: nil) unless key == "filter_ref"
.search.pull-right
- = form_tag project_graph_path(@project, @id), method: :get do |f|
+ = form_tag project_network_path(@project, @id), method: :get do |f|
.control-group
= label_tag :search , "Looking for commit:", class: 'control-label light'
.controls
diff --git a/app/views/graph/show.html.haml b/app/views/network/show.html.haml
index 0ee6648317c..a480ceaf995 100644
--- a/app/views/graph/show.html.haml
+++ b/app/views/network/show.html.haml
@@ -11,7 +11,7 @@
$(this).closest('form').submit();
});
branch_graph = new BranchGraph($("#holder"), {
- url: '#{project_graph_path(@project, @ref, @options.merge(format: :json))}',
+ url: '#{project_network_path(@project, @ref, @options.merge(format: :json))}',
commit_url: '#{project_commit_path(@project, 'ae45ca32').gsub("ae45ca32", "%s")}',
ref: '#{@ref}',
commit_id: '#{@commit.id}'
diff --git a/app/views/graph/show.json.erb b/app/views/network/show.json.erb
index 9a62cdb3dc9..9a62cdb3dc9 100644
--- a/app/views/graph/show.json.erb
+++ b/app/views/network/show.json.erb
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/_clone_panel.html.haml b/app/views/projects/_clone_panel.html.haml
index f8276c3c2b6..ebce06edf74 100644
--- a/app/views/projects/_clone_panel.html.haml
+++ b/app/views/projects/_clone_panel.html.haml
@@ -7,11 +7,12 @@
- unless @project.empty_repo?
- if can?(current_user, :fork_project, @project) && @project.namespace != current_user.namespace
- if current_user.already_forked?(@project)
- = link_to project_path(current_user.fork_of(@project)), class: 'btn grouped btn-primary' do
+ = link_to project_path(current_user.fork_of(@project)), class: 'btn grouped disabled' do
+ %i.icon-code-fork
Forked
- else
= link_to fork_project_path(@project), title: "Fork", class: "btn grouped", method: "POST" do
- %i.icon-copy
+ %i.icon-code-fork
Fork
- if can? current_user, :download_code, @project
= link_to archive_project_repository_path(@project), class: "btn grouped" do
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/fork.html.haml b/app/views/projects/fork.html.haml
new file mode 100644
index 00000000000..a1c109e5d62
--- /dev/null
+++ b/app/views/projects/fork.html.haml
@@ -0,0 +1,19 @@
+.alert.alert-error.alert-block
+ %h4
+ %i.icon-code-fork
+ Fork Error!
+ %p
+ You are trying to fork
+ = link_to_project @project
+ but it fails due to next reason:
+
+
+ - if @forked_project && @forked_project.errors.any?
+ %p
+ &ndash;
+ = @forked_project.errors.full_messages.first
+
+ %p
+ = link_to fork_project_path(@project), title: "Fork", class: "btn", method: "POST" do
+ %i.icon-code-fork
+ Try to Fork again
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index f72ef0a99ae..7d708ce7b8f 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -42,6 +42,7 @@
%p Owner: #{link_to @project.owner_name, @project.owner}
- if @project.forked_from_project
%p
+ %i.icon-code-fork
Forked from:
= link_to @project.forked_from_project.name_with_namespace, project_path(@project.forked_from_project)
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/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml
index 62e0fa25898..5120902fa0e 100644
--- a/app/views/shared/_clone_panel.html.haml
+++ b/app/views/shared/_clone_panel.html.haml
@@ -4,10 +4,8 @@
= text_field_tag :project_clone, @project.url_to_repo, class: "one_click_select span7", readonly: true
%span.add-on
- if @project.public
- .cblue
- %i.icon-share
- public
+ = public_icon
+ %span.cblue public
- else
- .cgreen
- %i.icon-lock
- private
+ = private_icon
+ %span.cgreen private
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/database.yml.mysql b/config/database.yml.mysql
index 436bea77f0f..a3eff1a74f8 100644
--- a/config/database.yml.mysql
+++ b/config/database.yml.mysql
@@ -6,7 +6,7 @@ production:
encoding: utf8
reconnect: false
database: gitlabhq_production
- pool: 5
+ pool: 10
username: root
password: "secure password"
# host: localhost
diff --git a/config/database.yml.postgresql b/config/database.yml.postgresql
index 4a4aa3465a6..4b74f3348f8 100644
--- a/config/database.yml.postgresql
+++ b/config/database.yml.postgresql
@@ -5,7 +5,7 @@ production:
adapter: postgresql
encoding: unicode
database: gitlabhq_production
- pool: 5
+ pool: 10
username: git
password:
# host: localhost
diff --git a/config/environments/production.rb b/config/environments/production.rb
index dc8e25593ae..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/initializers/2_app.rb b/config/initializers/2_app.rb
index 27a0c0ffeb2..e2f98002347 100644
--- a/config/initializers/2_app.rb
+++ b/config/initializers/2_app.rb
@@ -6,3 +6,8 @@ module Gitlab
Settings
end
end
+
+#
+# Load all libs for threadsafety
+#
+Dir["#{Rails.root}/lib/**/*.rb"].each { |file| require file }
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 c555cd0b361..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
@@ -190,8 +191,8 @@ Gitlab::Application.routes.draw do
resources :commits, only: [:show], constraints: {id: /(?:[^.]|\.(?!atom$))+/, format: /atom/}
resources :compare, only: [:index, :create]
resources :blame, only: [:show], constraints: {id: /.+/}
- resources :graph, only: [:show], constraints: {id: /(?:[^.]|\.(?!json$))+/, format: /json/}
- resources :stat_graph, only: [:show], constraints: {id: /(?:[^.]|\.(?!json$))+/, format: /json/}
+ resources :network, only: [:show], constraints: {id: /(?:[^.]|\.(?!json$))+/, format: /json/}
+ resources :graphs, only: [:show], constraints: {id: /(?:[^.]|\.(?!json$))+/, format: /json/}
match "/compare/:from...:to" => "compare#show", as: "compare", via: [:get, :post], constraints: {from: /.+/, to: /.+/}
scope module: :projects do
@@ -251,11 +252,11 @@ Gitlab::Application.routes.draw do
member do
# tree viewer logs
- get "logs_tree", constraints: { id: /[a-zA-Z.\/0-9_\-]+/ }
+ get "logs_tree", constraints: { id: /[a-zA-Z.\/0-9_\-#%+]+/ }
get "logs_tree/:path" => "refs#logs_tree",
as: :logs_file,
constraints: {
- id: /[a-zA-Z.0-9\/_\-]+/,
+ id: /[a-zA-Z.0-9\/_\-#%+]+/,
path: /.*/
}
end
@@ -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/20130611210815_increase_snippet_text_column_size.rb b/db/migrate/20130611210815_increase_snippet_text_column_size.rb
new file mode 100644
index 00000000000..f7b4447e43e
--- /dev/null
+++ b/db/migrate/20130611210815_increase_snippet_text_column_size.rb
@@ -0,0 +1,9 @@
+class IncreaseSnippetTextColumnSize < ActiveRecord::Migration
+ def up
+ # MYSQL LARGETEXT for snippet
+ change_column :snippets, :content, :text, :limit => 4294967295
+ end
+
+ def down
+ 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 2b3e40034ad..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/
@@ -188,6 +188,10 @@ do so with caution!
# Copy the example Puma config
sudo -u git -H cp config/puma.rb.example config/puma.rb
+ # 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 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
sudo -u git -H git config --global user.name "GitLab"
@@ -201,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
@@ -212,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
@@ -265,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
@@ -276,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
@@ -340,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/make_release.md b/doc/make_release.md
new file mode 100644
index 00000000000..24f8397f10b
--- /dev/null
+++ b/doc/make_release.md
@@ -0,0 +1,56 @@
+# Things to do when creating new release
+NOTE: This is a developer guide. If you are trying to install GitLab see the latest stable [installation guide](install/installation.md) and if you are trying to upgrade, see the [upgrade guides](update).
+## Install guide up to date?
+
+* References correct GitLab branch `x-x-stable` and correct GitLab shell tag?
+
+## Make upgrade guide
+
+### From x.x to x.x
+
+#### 0. Any major changes? Database updates? Web server change? File strucuture changes?
+
+#### 1. Make backup
+
+#### 2. Stop server
+
+#### 3. Do users need to update dependencies like `git`?
+
+#### 4. Get latest code
+
+#### 5. Does GitLab shell need to be updated?
+
+#### 6. Install libs, migrations, etc.
+
+#### 7. Any config files updated since last release?
+
+Check if any of these changed since last release (~22nd of last month depending on when last release branch was created):
+
+* https://github.com/gitlabhq/gitlabhq/commits/master/lib/support/nginx/gitlab
+* https://github.com/gitlabhq/gitlab-shell/commits/master/config.yml.example
+* https://github.com/gitlabhq/gitlabhq/commits/master/config/gitlab.yml.example
+* https://github.com/gitlabhq/gitlabhq/commits/master/config/puma.rb.example
+* https://github.com/gitlabhq/gitlabhq/commits/master/config/database.yml.mysql
+* https://github.com/gitlabhq/gitlabhq/commits/master/config/database.yml.postgresql
+
+#### 8. Need to update init script?
+
+Check if changed since last release (~22nd of last month depending on when last release branch was created): https://github.com/gitlabhq/gitlabhq/commits/master/lib/support/init.d/gitlab
+
+#### 9. Start application
+
+#### 10. Check application status
+
+## Make sure code status is good
+
+* [![build status](http://ci.gitlab.org/projects/1/status.png?ref=master)](http://ci.gitlab.org/projects/1?ref=master) on ci.gitlab.org (master branch)
+
+* [![build status](https://secure.travis-ci.org/gitlabhq/gitlabhq.png)](https://travis-ci.org/gitlabhq/gitlabhq) on travis-ci.org (master branch)
+
+* [![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)
+
+* [![Coverage Status](https://coveralls.io/repos/gitlabhq/gitlabhq/badge.png?branch=master)](https://coveralls.io/r/gitlabhq/gitlabhq)
+
+## Make release branch
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/admin/groups.feature b/features/admin/groups.feature
index 28f35e3a831..054dccfd64c 100644
--- a/features/admin/groups.feature
+++ b/features/admin/groups.feature
@@ -11,6 +11,7 @@ Feature: Admin Groups
Then I should be redirected to group page
And I should see newly created group
+ @javascript
Scenario: Add user into projects in group
When I visit admin group page
When I select user "John" from user list as "Reporter"
diff --git a/features/group/group.feature b/features/group/group.feature
index a48affe8e02..64424f47236 100644
--- a/features/group/group.feature
+++ b/features/group/group.feature
@@ -19,6 +19,7 @@ Feature: Groups
When I visit group merge requests page
Then I should see merge requests from this group assigned to me
+ @javascript
Scenario: I should add user to projects in Group
Given I have new user "John"
When I visit group people page
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/admin/admin_groups.rb b/features/steps/admin/admin_groups.rb
index a2b49070f9f..d780d9c96d9 100644
--- a/features/steps/admin/admin_groups.rb
+++ b/features/steps/admin/admin_groups.rb
@@ -2,6 +2,7 @@ class AdminGroups < Spinach::FeatureSteps
include SharedAuthentication
include SharedPaths
include SharedActiveTab
+ include Select2Helper
When 'I visit admin group page' do
visit admin_group_path(current_group)
@@ -40,8 +41,8 @@ class AdminGroups < Spinach::FeatureSteps
When 'I select user "John" from user list as "Reporter"' do
user = User.find_by_name("John")
+ select2(user.id, from: "#user_ids", multiple: true)
within "#new_team_member" do
- select user.name, from: "user_ids"
select "Reporter", from: "project_access"
end
click_button "Add user to projects in group"
@@ -49,8 +50,6 @@ class AdminGroups < Spinach::FeatureSteps
Then 'I should see "John" in team list in every project as "Reporter"' do
user = User.find_by_name("John")
- projects_with_access = find(".user_#{user.id} .projects_access")
- projects_with_access.should have_link("Reporter")
end
protected
diff --git a/features/steps/admin/admin_teams.rb b/features/steps/admin/admin_teams.rb
index 65c7e485f48..066fc3fa603 100644
--- a/features/steps/admin/admin_teams.rb
+++ b/features/steps/admin/admin_teams.rb
@@ -85,7 +85,7 @@ class AdminTeams < Spinach::FeatureSteps
end
Then 'I should see empty projects table' do
- page.has_no_css?("#projects_list").must_equal true
+ page.should have_content "Projects (0)"
end
When 'I select project "Shop" with max access "Reporter"' do
diff --git a/features/steps/group/group.rb b/features/steps/group/group.rb
index 8b5a4ed44df..102bc440d82 100644
--- a/features/steps/group/group.rb
+++ b/features/steps/group/group.rb
@@ -1,6 +1,7 @@
class Groups < Spinach::FeatureSteps
include SharedAuthentication
include SharedPaths
+ include Select2Helper
Then 'I should see projects list' do
current_user.authorized_projects.each do |project|
@@ -39,7 +40,7 @@ class Groups < Spinach::FeatureSteps
And 'I select user "John" from list with role "Reporter"' do
user = User.find_by_name("John")
within "#new_team_member" do
- select user.name, from: "user_ids"
+ select2(user.id, from: "#user_ids", multiple: true)
select "Reporter", from: "project_access"
end
click_button "Add"
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_graph.rb b/features/steps/project/project_graph.rb
index 489485b1dc4..50942b3cbb3 100644
--- a/features/steps/project/project_graph.rb
+++ b/features/steps/project/project_graph.rb
@@ -8,6 +8,6 @@ class ProjectGraph < Spinach::FeatureSteps
When 'I visit project "Shop" graph page' do
project = Project.find_by_name("Shop")
- visit project_stat_graph_path(project, "master")
+ visit project_graph_path(project, "master")
end
end
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/project/project_network_graph.rb b/features/steps/project/project_network_graph.rb
index 48a73f09fac..f001c0beb9a 100644
--- a/features/steps/project/project_network_graph.rb
+++ b/features/steps/project/project_network_graph.rb
@@ -12,7 +12,7 @@ class ProjectNetworkGraph < Spinach::FeatureSteps
Network::Graph.stub(max_count: 10)
project = Project.find_by_name("Shop")
- visit project_graph_path(project, "master")
+ visit project_network_path(project, "master")
end
And 'page should select "master" in select box' do
diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb
index 3641e788662..21b6159bce3 100644
--- a/features/steps/shared/paths.rb
+++ b/features/steps/shared/paths.rb
@@ -149,7 +149,7 @@ module SharedPaths
# Stub Graph max_size to speed up test (10 commits vs. 650)
Network::Graph.stub(max_count: 10)
- visit project_graph_path(@project, root_ref)
+ visit project_network_path(@project, root_ref)
end
step "I visit my project's issues page" do
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/issues.rb b/lib/api/issues.rb
index a2983e197e7..a15203d1563 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -2,6 +2,7 @@ module API
# Issues API
class Issues < Grape::API
before { authenticate! }
+ before { Thread.current[:current_user] = current_user }
resource :issues do
# Get currently authenticated user's issues
@@ -79,7 +80,7 @@ module API
attrs = attributes_for_keys [:title, :description, :assignee_id, :milestone_id, :state_event]
attrs[:label_list] = params[:labels] if params[:labels].present?
- IssueObserver.current_user = current_user
+
if @issue.update_attributes attrs
present @issue, with: Entities::Issue
else
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index 23e2f82889f..861a4f4d159 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -2,6 +2,7 @@ module API
# MergeRequest API
class MergeRequests < Grape::API
before { authenticate! }
+ before { Thread.current[:current_user] = current_user }
resource :projects do
helpers do
@@ -94,8 +95,6 @@ module API
authorize! :modify_merge_request, merge_request
- MergeRequestObserver.current_user = current_user
-
if merge_request.update_attributes attrs
merge_request.reload_code
merge_request.mark_as_unchecked
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/backup/repository.rb b/lib/backup/repository.rb
index 62a510f2acc..c5e3d049fd7 100644
--- a/lib/backup/repository.rb
+++ b/lib/backup/repository.rb
@@ -71,7 +71,7 @@ module Backup
print 'Put GitLab hooks in repositories dirs'.yellow
gitlab_shell_user_home = File.expand_path("~#{Gitlab.config.gitlab_shell.ssh_user}")
- if system("#{gitlab_shell_user_home}/gitlab-shell/support/rewrite-hooks.sh")
+ if system("#{gitlab_shell_user_home}/gitlab-shell/support/rewrite-hooks.sh #{Gitlab.config.gitlab_shell.repos_path}")
puts " [DONE]".green
else
puts " [FAILED]".red
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
new file mode 100644
index 00000000000..59203b2fbd6
--- /dev/null
+++ b/lib/gitlab/blacklist.rb
@@ -0,0 +1,9 @@
+module Gitlab
+ module Blacklist
+ extend self
+
+ def path
+ %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/gitlab/inline_diff.rb b/lib/gitlab/inline_diff.rb
index b39fd0d552d..44cf49b4047 100644
--- a/lib/gitlab/inline_diff.rb
+++ b/lib/gitlab/inline_diff.rb
@@ -21,8 +21,9 @@ module Gitlab
end
end
first_token = first_line[0..first_the_same_symbols][1..-1]
- diff_arr[index+1].sub!(first_token, first_token + START)
- diff_arr[index+2].sub!(first_token, first_token + START)
+ start = first_token + START
+ diff_arr[index+1].sub!(first_token, first_token => start)
+ diff_arr[index+2].sub!(first_token, first_token => start)
last_the_same_symbols = 0
(1..max_length + 1).each do |i|
last_the_same_symbols = -i
@@ -60,8 +61,6 @@ module Gitlab
line.gsub!(FINISH, "</span>")
line
end
-
end
-
end
end
diff --git a/lib/gitlab/version_info.rb b/lib/gitlab/version_info.rb
index 31b72720972..6ee41e85cc9 100644
--- a/lib/gitlab/version_info.rb
+++ b/lib/gitlab/version_info.rb
@@ -5,7 +5,7 @@ module Gitlab
attr_reader :major, :minor, :patch
def self.parse(str)
- if m = str.match(/(\d+)\.(\d+)\.(\d+)/)
+ if str && m = str.match(/(\d+)\.(\d+)\.(\d+)/)
VersionInfo.new(m[1].to_i, m[2].to_i, m[3].to_i)
else
VersionInfo.new
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/backup.rake b/lib/tasks/gitlab/backup.rake
index 65d99d1aea3..d071938acb5 100644
--- a/lib/tasks/gitlab/backup.rake
+++ b/lib/tasks/gitlab/backup.rake
@@ -90,13 +90,21 @@ namespace :gitlab do
settings = YAML.load_file("backup_information.yml")
ENV["VERSION"] = "#{settings[:db_version]}" if settings[:db_version].to_i > 0
- # restoring mismatching backups can lead to unexpected problems
- if settings[:gitlab_version] != %x{git rev-parse HEAD}.gsub(/\n/,"")
- puts "GitLab version mismatch:".red
- puts " Your current HEAD differs from the HEAD in the backup!".red
- puts " Please switch to the following revision and try again:".red
- puts " revision: #{settings[:gitlab_version]}".red
- exit 1
+ # backups directory is not always sub of Rails root and able to execute the git rev-parse below
+ begin
+ Dir.chdir(Rails.root)
+
+ # restoring mismatching backups can lead to unexpected problems
+ if settings[:gitlab_version] != %x{git rev-parse HEAD}.gsub(/\n/, "")
+ puts "GitLab version mismatch:".red
+ puts " Your current HEAD differs from the HEAD in the backup!".red
+ puts " Please switch to the following revision and try again:".red
+ puts " revision: #{settings[:gitlab_version]}".red
+ exit 1
+ end
+ ensure
+ # chdir back to original intended dir
+ Dir.chdir(Gitlab.config.backup.path)
end
Rake::Task["gitlab:backup:db:restore"].invoke
diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake
index 38e8e367904..3d96eab0149 100644
--- a/lib/tasks/gitlab/check.rake
+++ b/lib/tasks/gitlab/check.rake
@@ -661,7 +661,7 @@ namespace :gitlab do
current_version = Gitlab::VersionInfo.parse(gitlab_shell_version)
print "GitLab Shell version >= #{required_version} ? ... "
- if required_version <= current_version
+ if current_version.valid? && required_version <= current_version
puts "OK (#{current_version})".green
else
puts "FAIL. Please update gitlab-shell to #{required_version} from #{current_version}".red
@@ -675,7 +675,7 @@ namespace :gitlab do
puts "Your git bin path is \"#{Gitlab.config.git.bin_path}\""
print "Git version >= #{required_version} ? ... "
- if required_version <= current_version
+ if current_version.valid? && required_version <= current_version
puts "yes (#{current_version})".green
else
puts "no".red
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/lib/tasks/gitlab/shell.rake b/lib/tasks/gitlab/shell.rake
index ec5451dd47c..11d4eacaa69 100644
--- a/lib/tasks/gitlab/shell.rake
+++ b/lib/tasks/gitlab/shell.rake
@@ -26,10 +26,12 @@ namespace :gitlab do
warn_user_is_not_gitlab
gitlab_shell_authorized_keys = File.join(File.expand_path("~#{Gitlab.config.gitlab_shell.ssh_user}"),'.ssh/authorized_keys')
- puts "This will rebuild an authorized_keys file."
- puts "You will lose any data stored in #{gitlab_shell_authorized_keys}."
- ask_to_continue
- puts ""
+ unless ENV['force'] == 'yes'
+ puts "This will rebuild an authorized_keys file."
+ puts "You will lose any data stored in #{gitlab_shell_authorized_keys}."
+ ask_to_continue
+ puts ""
+ end
system("echo '# Managed by gitlab-shell' > #{gitlab_shell_authorized_keys}")
diff --git a/script/check b/script/check
index d2eb4a2f6d8..c907a98b5d9 100755
--- a/script/check
+++ b/script/check
@@ -1,2 +1,2 @@
#!/bin/sh
-sudo -u gitlab -H bundle exec rake gitlab:check RAILS_ENV=production
+sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
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/models/milestone_spec.rb b/spec/models/milestone_spec.rb
index efab5510c59..d2819b7c1d6 100644
--- a/spec/models/milestone_spec.rb
+++ b/spec/models/milestone_spec.rb
@@ -39,7 +39,6 @@ describe Milestone do
end
it "should count closed issues" do
- IssueObserver.current_user = issue.author
issue.close
milestone.issues << issue
milestone.percent_complete.should == 100
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 94f9480a4d0..0b6528bfc81 100644
--- a/spec/routing/project_routing_spec.rb
+++ b/spec/routing/project_routing_spec.rb
@@ -201,7 +201,11 @@ describe RefsController, "routing" do
it "to #logs_tree" do
get("/gitlabhq/refs/stable/logs_tree").should route_to('refs#logs_tree', project_id: 'gitlabhq', id: 'stable')
+ get("/gitlabhq/refs/feature%2345/logs_tree").should route_to('refs#logs_tree', project_id: 'gitlabhq', id: 'feature#45')
+ get("/gitlabhq/refs/feature%2B45/logs_tree").should route_to('refs#logs_tree', project_id: 'gitlabhq', id: 'feature+45')
get("/gitlabhq/refs/stable/logs_tree/foo/bar/baz").should route_to('refs#logs_tree', project_id: 'gitlabhq', id: 'stable', path: 'foo/bar/baz')
+ get("/gitlabhq/refs/feature%2345/logs_tree/foo/bar/baz").should route_to('refs#logs_tree', project_id: 'gitlabhq', id: 'feature#45', path: 'foo/bar/baz')
+ get("/gitlabhq/refs/feature%2B45/logs_tree/foo/bar/baz").should route_to('refs#logs_tree', project_id: 'gitlabhq', id: 'feature+45', path: 'foo/bar/baz')
get("/gitlab/gitlabhq/refs/stable/logs_tree/files.scss").should route_to('refs#logs_tree', project_id: 'gitlab/gitlabhq', id: 'stable', path: 'files.scss')
end
end
@@ -258,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
@@ -446,9 +450,15 @@ describe CompareController, "routing" do
end
end
-describe GraphController, "routing" do
+describe NetworkController, "routing" do
it "to #show" do
- get("/gitlabhq/graph/master").should route_to('graph#show', project_id: 'gitlabhq', id: 'master')
- get("/gitlabhq/graph/master.json").should route_to('graph#show', project_id: 'gitlabhq', id: 'master', format: "json")
+ get("/gitlabhq/network/master").should route_to('network#show', project_id: 'gitlabhq', id: 'master')
+ get("/gitlabhq/network/master.json").should route_to('network#show', project_id: 'gitlabhq', id: 'master', format: "json")
+ end
+end
+
+describe GraphsController, "routing" do
+ it "to #show" do
+ get("/gitlabhq/graphs/master").should route_to('graphs#show', project_id: 'gitlabhq', id: 'master')
end
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