OSDN Git Service

Merge branch 'master' into fixes/api
authorSebastian Ziebell <sebastian.ziebell@asquera.de>
Tue, 5 Mar 2013 21:29:49 +0000 (22:29 +0100)
committerSebastian Ziebell <sebastian.ziebell@asquera.de>
Tue, 5 Mar 2013 21:29:49 +0000 (22:29 +0100)
Conflicts:
lib/api/projects.rb

146 files changed:
CONTRIBUTING.md
Gemfile
Gemfile.lock
README.md
ROADMAP.md
app/assets/fonts/OFL.txt [deleted file]
app/assets/fonts/YanoneKaffeesatz-Light.ttf [deleted file]
app/assets/javascripts/branch-graph.js [moved from vendor/assets/javascripts/branch-graph.js with 89% similarity]
app/assets/javascripts/main.js.coffee
app/assets/javascripts/projects.js.coffee
app/assets/stylesheets/common.scss
app/assets/stylesheets/gitlab_bootstrap/common.scss
app/assets/stylesheets/gitlab_bootstrap/fonts.scss
app/assets/stylesheets/gitlab_bootstrap/mixins.scss
app/assets/stylesheets/gitlab_bootstrap/typography.scss
app/assets/stylesheets/highlight/dark.scss
app/assets/stylesheets/sections/commits.scss
app/assets/stylesheets/sections/events.scss
app/assets/stylesheets/sections/header.scss
app/assets/stylesheets/sections/login.scss
app/assets/stylesheets/sections/notes.scss
app/assets/stylesheets/sections/projects.scss
app/contexts/issues_list_context.rb
app/controllers/admin/teams/members_controller.rb
app/controllers/admin/users_controller.rb
app/controllers/application_controller.rb
app/controllers/commits_controller.rb
app/controllers/compare_controller.rb
app/controllers/graph_controller.rb
app/controllers/merge_requests_controller.rb
app/controllers/milestones_controller.rb
app/controllers/projects_controller.rb
app/controllers/teams/members_controller.rb
app/decorators/application_decorator.rb
app/helpers/application_helper.rb
app/helpers/issues_helper.rb
app/helpers/tree_helper.rb
app/models/ability.rb
app/models/graph/commit.rb [new file with mode: 0644]
app/models/graph/json_builder.rb [new file with mode: 0644]
app/models/group.rb
app/models/issue.rb
app/models/namespace.rb
app/models/project.rb
app/models/repository.rb
app/models/user.rb
app/models/user_team.rb
app/observers/issue_observer.rb
app/observers/user_observer.rb
app/services/git_push_service.rb
app/services/project_transfer_service.rb
app/views/admin/groups/edit.html.haml
app/views/admin/groups/index.html.haml
app/views/admin/groups/new.html.haml
app/views/admin/groups/show.html.haml
app/views/admin/projects/_form.html.haml
app/views/admin/teams/edit.html.haml
app/views/admin/teams/index.html.haml
app/views/admin/teams/new.html.haml
app/views/admin/teams/show.html.haml
app/views/admin/users/_form.html.haml
app/views/admin/users/index.html.haml
app/views/admin/users/show.html.haml
app/views/commits/_commits.html.haml
app/views/events/event/_note.html.haml
app/views/groups/edit.html.haml
app/views/groups/new.html.haml
app/views/groups/show.html.haml
app/views/issues/_filter.html.haml
app/views/layouts/_flash.html.haml
app/views/layouts/_head.html.haml
app/views/layouts/_head_panel.html.haml
app/views/layouts/admin.html.haml
app/views/layouts/application.html.haml
app/views/layouts/devise.html.haml
app/views/layouts/errors.html.haml
app/views/layouts/group.html.haml
app/views/layouts/profile.html.haml
app/views/layouts/project_resource.html.haml
app/views/layouts/user_team.html.haml
app/views/merge_requests/show/_mr_accept.html.haml
app/views/notify/issue_status_changed_email.text.erb [new file with mode: 0644]
app/views/notify/new_issue_email.text.erb [new file with mode: 0644]
app/views/notify/new_merge_request_email.text.erb [new file with mode: 0644]
app/views/notify/new_user_email.text.erb [new file with mode: 0644]
app/views/notify/note_commit_email.text.erb [new file with mode: 0644]
app/views/notify/note_issue_email.text.erb [new file with mode: 0644]
app/views/notify/note_merge_request_email.text.erb [new file with mode: 0644]
app/views/notify/note_wall_email.text.erb [new file with mode: 0644]
app/views/notify/project_access_granted_email.text.erb [new file with mode: 0644]
app/views/notify/project_was_moved_email.text.erb [new file with mode: 0644]
app/views/notify/reassigned_issue_email.text.erb [new file with mode: 0644]
app/views/notify/reassigned_merge_request_email.text.erb [new file with mode: 0644]
app/views/projects/_form.html.haml
app/views/projects/empty.html.haml
app/views/public/projects/index.html.haml
app/views/shared/_clone_panel.html.haml
app/views/team_members/_team_member.html.haml
app/views/teams/edit.html.haml
app/views/teams/members/_show.html.haml
app/views/teams/new.html.haml
app/views/teams/show.html.haml
config/gitlab.yml.example
config/initializers/1_settings.rb
config/initializers/devise.rb
config/locales/devise.en.yml
config/routes.rb
db/migrate/20130123114545_add_issues_tracker_to_project.rb [new file with mode: 0644]
db/migrate/20130206084024_add_description_to_namsespace.rb [new file with mode: 0644]
db/migrate/20130207104426_add_description_to_teams.rb [new file with mode: 0644]
db/migrate/20130211085435_add_issues_tracker_id_to_project.rb [new file with mode: 0644]
db/migrate/20130218141258_convert_closed_to_state_in_issue.rb
db/migrate/20130218141327_convert_closed_to_state_in_merge_request.rb
db/migrate/20130218141344_convert_closed_to_state_in_milestone.rb
db/migrate/20130304104623_add_state_to_user.rb [new file with mode: 0644]
db/migrate/20130304104740_convert_blocked_to_state.rb [new file with mode: 0644]
db/migrate/20130304105317_remove_blocked_from_user.rb [new file with mode: 0644]
db/schema.rb
doc/install/databases.md
doc/install/installation.md
features/project/network.feature
features/steps/admin/admin_groups.rb
features/steps/admin/admin_teams.rb
features/steps/group/group.rb
features/steps/project/project_merge_requests.rb
features/steps/project/project_network_graph.rb
features/steps/shared/paths.rb
features/steps/userteams/userteams.rb
features/support/env.rb
features/teams/team.feature
lib/api/entities.rb
lib/api/projects.rb
lib/extracts_path.rb
lib/gitlab/auth.rb
lib/gitlab/graph/commit.rb [deleted file]
lib/gitlab/graph/json_builder.rb [deleted file]
lib/gitlab/markdown.rb
lib/tasks/sidekiq.rake
spec/factories.rb
spec/factories/user_teams.rb
spec/features/admin/admin_users_spec.rb
spec/helpers/gitlab_markdown_helper_spec.rb
spec/helpers/issues_helper_spec.rb [new file with mode: 0644]
spec/models/project_spec.rb
spec/models/user_spec.rb
spec/observers/user_observer_spec.rb

index 00304dd..cff5e93 100644 (file)
@@ -1,26 +1,76 @@
-# Contact & support
+# Contribute to GitLab
 
-If you want quick help, head over to our [Support Forum](https://groups.google.com/forum/#!forum/gitlabhq).
-Otherwise you can follow our [Issue Submission Guide](https://github.com/gitlabhq/gitlabhq/wiki/Issue-Submission-Guide) for a more systematic and thorough guide to solving your issues.
+If you have a question or want to contribute to GitLab this guide show you the appropriate channel to use.
 
+## Ruling out common errors
 
+Some errors are common and it may so happen, that you are not the only one who stumbled over a particular issue. We have [collected several of those and documented quick solutions](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide) for them.
 
-# Contribute to GitLab
+## Support forum
+
+Please visit our [Support Forum](https://groups.google.com/forum/#!forum/gitlabhq) for any kind of question regarding the usage or adiministration/configuration of GitLab.
+
+### Use the support forum if ...
 
-## Recipes
+* You get permission denied errors
+* You can't see your repos
+* You have issues cloning, pulling or pushing
+* You have issues with web_hooks not firing
 
-We collect user submitted installation scripts and config file templates for platforms we don't support officially.
-We believe there is merit in allowing a certain amount of diversity.
-You can get and submit your solution to running/configuring GitLab with your favorite OS/distro, database, web server, cloud hoster, configuration management tool, etc.
+**Search** for similar issues before posting your own, there's a good chance somebody else had the same issue you have now and had it resolved.
 
-Help us improve the collection of [GitLab Recipes](https://github.com/gitlabhq/gitlab-recipes/)
+## Paid support
 
+Community support in the [Support Forum](https://groups.google.com/forum/#!forum/gitlabhq) is done by volunteers. Paid support is available from [GitLab.com](http://blog.gitlab.com/services/)
 
 ## Feature suggestions
 
-Follow the [Issue Submission Guide](https://github.com/gitlabhq/gitlabhq/wiki/Issue-Submission-Guide) and support other peoples ideas or propose your own.
+Feature suggestions don't belong in issues but can go to [Feedback forum](http://gitlab.uservoice.com/forums/176466-general) where they can be voted on.
+
+## Pull requests
+
+Code speaks louder than words. If you can please submit a pull request with the fix including tests. The workflow to make a pull request is as follows:
+
+1. Fork the project on GitHub
+1. Create a feature branch
+1. Write tests and code
+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
+
+We will accept pull requests if:
+
+* The code has proper tests and all tests pass
+* 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
+* The description includes a motive for your change and the method you used to achieve it
+* It keeps the GitLab code base clean and well structured
+* We think other users will need the same functionality
+* If it makes changes to the UI the pull request should include screenshots
+
+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).
+
+## Submitting via GitHub's issue tracker
+
+* For obvious bugs or misbehavior in GitLab in the master branch. Please include the revision id and a reproducible test case.
+* For problematic or insufficient documentation. Please give a suggestion on how to improve it.
+
+If you're unsure where to post, post it to the [Support Forum](https://groups.google.com/forum/#!forum/gitlabhq) first.
+There are a lot of helpful GitLab users there who may be able to help you quickly.
+If your particular issue turns out to be a bug, it will find its way from there to the [issue tracker on GitHub](https://github.com/gitlabhq/gitlabhq/issues).
+
+### When submitting an issue
+
+**Search** for similar entries before submitting your own, there's a good chance somebody else had the same issue or idea. Show your support with `:+1:` and/or join the discussion.
+
+Please consider the following points when submitting an **issue**:
 
+* Summarize your issue in one sentence (what happened wrong, when you did/expected something else)
+* Describe your issue in detail (including steps to reproduce)
+* Add logs or screen shots when possible
+* Describe your setup (use relevant parts from `sudo -u gitlab -H bundle exec rake gitlab:env:info`)
 
-## Code
+## Thank you!
 
-Follow our [Developer Guide](https://github.com/gitlabhq/gitlabhq/wiki/Developer-Guide) to set you up for hacking on GitLab.
+By taking the time to use the right channel, you help the development team to organize and prioritize issues and suggestions in order to make GitLab a better product for us all.
diff --git a/Gemfile b/Gemfile
index 6652d1e..cf1617c 100644 (file)
--- a/Gemfile
+++ b/Gemfile
@@ -15,16 +15,18 @@ gem "mysql2", group: :mysql
 gem "pg", group: :postgres
 
 # Auth
-gem "devise", "~> 2.1.0"
-gem 'omniauth', "~> 1.1.1"
+gem "devise"
+gem 'omniauth', "~> 1.1.3"
 gem 'omniauth-google-oauth2'
 gem 'omniauth-twitter'
 gem 'omniauth-github'
 
-# GITLAB patched libs
-gem "grit",          git: "https://github.com/gitlabhq/grit.git",           ref: '9e98418ce2d654485b967003726aa2706a10060b'
-gem 'grack',         git: "https://github.com/gitlabhq/grack.git",          ref: 'ba46f3b0845c6a09d488ae6abdce6ede37e227e8'
-gem 'grit_ext',      git: "https://github.com/gitlabhq/grit_ext.git",       ref: '8e6afc2da821354774aa4d1ee8a1aa2082f84a3e'
+# Extracting information from a git repository
+gem "gitlab-grit", '~> 1.0.0', require: 'grit'
+gem 'grit_ext', '~> 0.6.2'
+
+# Ruby/Rack Git Smart-HTTP Server Handler
+gem 'gitlab-grack', '~> 1.0.0', require: 'grack'
 
 # LDAP Auth
 gem 'gitlab_omniauth-ldap', '1.0.2', require: "omniauth-ldap"
@@ -33,7 +35,7 @@ gem 'gitlab_omniauth-ldap', '1.0.2', require: "omniauth-ldap"
 gem 'gitlab_yaml_db', '1.0.0', require: "yaml_db"
 
 # Syntax highlighter
-gem "pygments.rb",  git: "https://github.com/gitlabhq/pygments.rb.git", branch: "master"
+gem "gitlab-pygments.rb", '~> 0.3.2', require: 'pygments.rb'
 
 # Language detection
 gem "github-linguist", "~> 2.3.4" , require: "linguist"
@@ -46,14 +48,17 @@ gem "grape-entity", "~> 0.2.0"
 # based on human-friendly examples
 gem "stamp"
 
+# Enumeration fields
+gem 'enumerize'
+
 # Pagination
 gem "kaminari", "~> 0.14.1"
 
 # HAML
-gem "haml-rails", "~> 0.3.5"
+gem "haml-rails"
 
 # Files attachments
-gem "carrierwave", "~> 0.7.1"
+gem "carrierwave"
 
 # Authorization
 gem "six"
@@ -69,7 +74,7 @@ gem "redcarpet",     "~> 2.2.2"
 gem "github-markup", "~> 0.7.4", require: 'github/markup'
 
 # Servers
-gem "unicorn", "~> 4.4.0"
+gem "unicorn"
 
 # State machine
 gem "state_machine"
@@ -78,12 +83,12 @@ gem "state_machine"
 gem "acts-as-taggable-on", "2.3.3"
 
 # Decorators
-gem "draper", "~> 0.18.0"
+gem "draper"
 
 # Background jobs
 gem 'slim'
 gem 'sinatra', require: nil
-gem 'sidekiq', '2.7.3'
+gem 'sidekiq'
 
 # HTTP requests
 gem "httparty"
@@ -113,6 +118,7 @@ group :assets do
   gem 'bootstrap-sass',   "2.2.1.1"
   gem "font-awesome-sass-rails", "~> 3.0.0"
   gem "gemoji", "~> 1.2.1", require: 'emoji/railtie'
+  gem "gon"
 end
 
 group :development do
@@ -140,7 +146,7 @@ group :development, :test do
   gem "capybara", '2.0.2'
   gem "pry"
   gem "awesome_print"
-  gem "database_cleaner", ref: "9f898fc50d87a5d51760f9dcf374bf5ffda21baf", git: "https://github.com/bmabey/database_cleaner.git"
+  gem "database_cleaner"
   gem "launchy"
   gem 'factory_girl_rails'
 
index 93abf85..8988249 100644 (file)
@@ -1,11 +1,4 @@
 GIT
-  remote: https://github.com/bmabey/database_cleaner.git
-  revision: 9f898fc50d87a5d51760f9dcf374bf5ffda21baf
-  ref: 9f898fc50d87a5d51760f9dcf374bf5ffda21baf
-  specs:
-    database_cleaner (0.9.1)
-
-GIT
   remote: https://github.com/ctran/annotate_models.git
   revision: be4e26825b521f0b2d86b181e2dff89901aa9b1e
   specs:
@@ -14,41 +7,6 @@ GIT
       rake (>= 0.8.7)
 
 GIT
-  remote: https://github.com/gitlabhq/grack.git
-  revision: ba46f3b0845c6a09d488ae6abdce6ede37e227e8
-  ref: ba46f3b0845c6a09d488ae6abdce6ede37e227e8
-  specs:
-    grack (1.0.0)
-      rack (~> 1.4.1)
-
-GIT
-  remote: https://github.com/gitlabhq/grit.git
-  revision: 9e98418ce2d654485b967003726aa2706a10060b
-  ref: 9e98418ce2d654485b967003726aa2706a10060b
-  specs:
-    grit (2.5.0)
-      diff-lcs (~> 1.1)
-      mime-types (~> 1.15)
-      posix-spawn (~> 0.3.6)
-
-GIT
-  remote: https://github.com/gitlabhq/grit_ext.git
-  revision: 8e6afc2da821354774aa4d1ee8a1aa2082f84a3e
-  ref: 8e6afc2da821354774aa4d1ee8a1aa2082f84a3e
-  specs:
-    grit_ext (0.6.1)
-      charlock_holmes (~> 0.6.9)
-
-GIT
-  remote: https://github.com/gitlabhq/pygments.rb.git
-  revision: db1da0343adf86b49bdc3add04d02d2e80438d38
-  branch: master
-  specs:
-    pygments.rb (0.3.2)
-      posix-spawn (~> 0.3.6)
-      yajl-ruby (~> 1.1.0)
-
-GIT
   remote: https://github.com/gitlabhq/raphael-rails.git
   revision: cb2c92a040b9b941a5f1aa1ea866cc26e944fe58
   specs:
@@ -89,12 +47,13 @@ GEM
     addressable (2.3.2)
     arel (3.0.2)
     awesome_print (1.1.0)
-    backports (2.6.5)
+    backports (2.6.7)
     bcrypt-ruby (3.0.1)
     better_errors (0.3.2)
       coderay (>= 1.0.0)
       erubis (>= 2.7.0)
-    binding_of_caller (0.6.8)
+    binding_of_caller (0.7.1)
+      debug_inspector (>= 0.0.1)
     bootstrap-sass (2.2.1.1)
       sass (~> 3.2)
     builder (3.0.4)
@@ -105,7 +64,7 @@ GEM
       rack-test (>= 0.5.4)
       selenium-webdriver (~> 2.0)
       xpath (~> 1.0.0)
-    carrierwave (0.7.1)
+    carrierwave (0.8.0)
       activemodel (>= 3.2.0)
       activesupport (>= 3.2.0)
     celluloid (0.12.4)
@@ -132,18 +91,24 @@ GEM
     connection_pool (1.0.0)
     crack (0.3.1)
     daemons (1.1.9)
-    devise (2.1.2)
+    database_cleaner (0.9.1)
+    debug_inspector (0.0.2)
+    descendants_tracker (0.0.1)
+    devise (2.2.3)
       bcrypt-ruby (~> 3.0)
       orm_adapter (~> 0.1)
       railties (~> 3.1)
       warden (~> 1.2.1)
     diff-lcs (1.1.3)
-    draper (0.18.0)
-      actionpack (~> 3.2)
-      activesupport (~> 3.2)
+    draper (1.1.0)
+      actionpack (>= 3.0)
+      activesupport (>= 3.0)
+      request_store (~> 1.0.3)
     email_spec (1.4.0)
       launchy (~> 2.1)
       mail (~> 2.2)
+    enumerize (0.5.1)
+      activesupport (>= 3.2)
     erubis (2.7.0)
     escape_utils (0.2.4)
     eventmachine (1.0.0)
@@ -155,7 +120,7 @@ GEM
     factory_girl_rails (4.1.0)
       factory_girl (~> 4.1.0)
       railties (>= 3.0.0)
-    faraday (0.8.4)
+    faraday (0.8.6)
       multipart-post (~> 1.1)
     faye-websocket (0.4.7)
       eventmachine (>= 0.12.0)
@@ -164,7 +129,7 @@ GEM
     font-awesome-sass-rails (3.0.0.1)
       railties (>= 3.1.1)
       sass-rails (>= 3.1.1)
-    foreman (0.60.2)
+    foreman (0.61.0)
       thor (>= 0.13.6)
     gemoji (1.2.1)
     gherkin-ruby (0.2.1)
@@ -174,7 +139,16 @@ GEM
       escape_utils (~> 0.2.3)
       mime-types (~> 1.19)
       pygments.rb (>= 0.2.13)
-    github-markup (0.7.4)
+    github-markup (0.7.5)
+    gitlab-grack (1.0.0)
+      rack (~> 1.4.1)
+    gitlab-grit (1.0.0)
+      diff-lcs (~> 1.1)
+      mime-types (~> 1.15)
+      posix-spawn (~> 0.3.6)
+    gitlab-pygments.rb (0.3.2)
+      posix-spawn (~> 0.3.6)
+      yajl-ruby (~> 1.1.0)
     gitlab_meta (5.0)
     gitlab_omniauth-ldap (1.0.2)
       net-ldap (~> 0.2.2)
@@ -182,17 +156,22 @@ GEM
       pyu-ruby-sasl (~> 0.0.3.1)
       rubyntlm (~> 0.1.1)
     gitlab_yaml_db (1.0.0)
-    grape (0.3.1)
+    gon (4.0.2)
+    grape (0.3.2)
       activesupport
-      grape-entity (~> 0.2.0)
-      hashie (~> 1.2)
+      builder
+      hashie (>= 1.2.0)
       multi_json (>= 1.3.2)
-      multi_xml
+      multi_xml (>= 0.5.2)
       rack
       rack-accept
       rack-mount
       virtus
     grape-entity (0.2.0)
+      activesupport
+      multi_json (>= 1.3.2)
+    grit_ext (0.6.2)
+      charlock_holmes (~> 0.6.9)
     growl (1.0.3)
     guard (1.5.4)
       listen (>= 0.4.2)
@@ -205,20 +184,21 @@ GEM
     guard-spinach (0.0.2)
       guard (>= 1.1)
       spinach
-    haml (3.1.7)
-    haml-rails (0.3.5)
+    haml (4.0.0)
+      tilt
+    haml-rails (0.4)
       actionpack (>= 3.1, < 4.1)
       activesupport (>= 3.1, < 4.1)
-      haml (~> 3.1)
+      haml (>= 3.1, < 4.1)
       railties (>= 3.1, < 4.1)
     hashie (1.2.0)
     hike (1.2.1)
     http_parser.rb (0.5.3)
-    httparty (0.9.0)
+    httparty (0.10.2)
       multi_json (~> 1.0)
-      multi_xml
+      multi_xml (>= 0.5.2)
     httpauth (0.2.0)
-    i18n (0.6.1)
+    i18n (0.6.4)
     journey (1.0.4)
     jquery-atwho-rails (0.1.7)
     jquery-rails (2.1.3)
@@ -233,7 +213,7 @@ GEM
     kaminari (0.14.1)
       actionpack (>= 3.0.0)
       activesupport (>= 3.0.0)
-    kgio (2.7.4)
+    kgio (2.8.0)
     launchy (2.1.2)
       addressable (~> 2.3)
     letter_opener (1.0.0)
@@ -250,22 +230,22 @@ GEM
     modernizr (2.6.2)
       sprockets (~> 2.0)
     multi_json (1.6.1)
-    multi_xml (0.5.1)
-    multipart-post (1.1.5)
+    multi_xml (0.5.3)
+    multipart-post (1.2.0)
     mysql2 (0.3.11)
     net-ldap (0.2.2)
     nokogiri (1.5.6)
     oauth (0.4.7)
-    oauth2 (0.8.0)
+    oauth2 (0.8.1)
       faraday (~> 0.8)
       httpauth (~> 0.1)
       jwt (~> 0.1.4)
       multi_json (~> 1.0)
       rack (~> 1.2)
-    omniauth (1.1.1)
+    omniauth (1.1.3)
       hashie (~> 1.2)
       rack
-    omniauth-github (1.0.3)
+    omniauth-github (1.1.0)
       omniauth (~> 1.0)
       omniauth-oauth2 (~> 1.1)
     omniauth-google-oauth2 (0.1.13)
@@ -293,6 +273,9 @@ GEM
       coderay (~> 1.0.5)
       method_source (~> 0.8)
       slop (~> 3.3.1)
+    pygments.rb (0.4.2)
+      posix-spawn (~> 0.3.6)
+      yajl-ruby (~> 1.1.0)
     pyu-ruby-sasl (0.0.3.3)
     quiet_assets (1.0.1)
       railties (~> 3.1)
@@ -305,7 +288,7 @@ GEM
       rack (>= 1.1.3)
     rack-mount (0.8.3)
       rack (>= 1.0.0)
-    rack-protection (1.3.2)
+    rack-protection (1.4.0)
       rack
     rack-ssl (1.3.3)
       rack
@@ -342,12 +325,13 @@ GEM
     rb-fsevent (0.9.2)
     rb-inotify (0.8.8)
       ffi (>= 0.5.0)
-    rdoc (3.12.1)
+    rdoc (3.12.2)
       json (~> 1.4)
     redcarpet (2.2.2)
     redis (3.0.2)
     redis-namespace (1.2.1)
       redis (~> 3.0.0)
+    request_store (1.0.5)
     rspec (2.12.0)
       rspec-core (~> 2.12.0)
       rspec-expectations (~> 2.12.0)
@@ -381,11 +365,11 @@ GEM
       multi_json (~> 1.0)
       rubyzip
       websocket (~> 1.0.4)
-    settingslogic (2.0.8)
+    settingslogic (2.0.9)
     sexp_processor (4.1.3)
     shoulda-matchers (1.3.0)
       activesupport (>= 3.0.0)
-    sidekiq (2.7.3)
+    sidekiq (2.7.5)
       celluloid (~> 0.12.0)
       connection_pool (~> 1.0)
       multi_json (~> 1)
@@ -395,9 +379,9 @@ GEM
       multi_json (~> 1.0)
       simplecov-html (~> 0.7.1)
     simplecov-html (0.7.1)
-    sinatra (1.3.3)
-      rack (~> 1.3, >= 1.3.6)
-      rack-protection (~> 1.2)
+    sinatra (1.3.5)
+      rack (~> 1.4)
+      rack-protection (~> 1.3)
       tilt (~> 1.3, >= 1.3.3)
     six (0.2.0)
     slim (1.3.6)
@@ -416,7 +400,7 @@ GEM
       multi_json (~> 1.0)
       rack (~> 1.0)
       tilt (~> 1.1, != 1.3.0)
-    stamp (0.3.0)
+    stamp (0.5.0)
     state_machine (1.1.2)
     temple (0.5.5)
     test_after_commit (0.0.1)
@@ -427,7 +411,7 @@ GEM
       eventmachine (>= 0.12.6)
       rack (>= 1.0.0)
     thor (0.17.0)
-    tilt (1.3.3)
+    tilt (1.3.4)
     timers (1.1.0)
     treetop (1.4.12)
       polyglot
@@ -436,12 +420,13 @@ GEM
     uglifier (1.3.0)
       execjs (>= 0.3.0)
       multi_json (~> 1.0, >= 1.0.2)
-    unicorn (4.4.0)
+    unicorn (4.6.2)
       kgio (~> 2.6)
       rack
       raindrops (~> 0.7)
-    virtus (0.5.2)
+    virtus (0.5.4)
       backports (~> 2.6.1)
+      descendants_tracker (~> 0.0.1)
     warden (1.2.1)
       rack (>= 1.0)
     webmock (1.9.0)
@@ -463,14 +448,15 @@ DEPENDENCIES
   binding_of_caller
   bootstrap-sass (= 2.2.1.1)
   capybara (= 2.0.2)
-  carrierwave (~> 0.7.1)
+  carrierwave
   chosen-rails (= 0.9.8)
   coffee-rails (~> 3.2.2)
   colored
-  database_cleaner!
-  devise (~> 2.1.0)
-  draper (~> 0.18.0)
+  database_cleaner
+  devise
+  draper
   email_spec
+  enumerize
   factory_girl_rails
   ffaker
   font-awesome-sass-rails (~> 3.0.0)
@@ -479,18 +465,20 @@ DEPENDENCIES
   git
   github-linguist (~> 2.3.4)
   github-markup (~> 0.7.4)
+  gitlab-grack (~> 1.0.0)
+  gitlab-grit (~> 1.0.0)
+  gitlab-pygments.rb (~> 0.3.2)
   gitlab_meta (= 5.0)
   gitlab_omniauth-ldap (= 1.0.2)
   gitlab_yaml_db (= 1.0.0)
-  grack!
+  gon
   grape (~> 0.3.1)
   grape-entity (~> 0.2.0)
-  grit!
-  grit_ext!
+  grit_ext (~> 0.6.2)
   growl
   guard-rspec
   guard-spinach
-  haml-rails (~> 0.3.5)
+  haml-rails
   httparty
   jquery-atwho-rails (= 0.1.7)
   jquery-rails (= 2.1.3)
@@ -500,14 +488,13 @@ DEPENDENCIES
   letter_opener
   modernizr (= 2.6.2)
   mysql2
-  omniauth (~> 1.1.1)
+  omniauth (~> 1.1.3)
   omniauth-github
   omniauth-google-oauth2
   omniauth-twitter
   pg
   poltergeist (= 1.1.0)
   pry
-  pygments.rb!
   quiet_assets (~> 1.0.1)
   rack-mini-profiler
   rails (= 3.2.12)
@@ -523,7 +510,7 @@ DEPENDENCIES
   seed-fu
   settingslogic
   shoulda-matchers (= 1.3.0)
-  sidekiq (= 2.7.3)
+  sidekiq
   simplecov
   sinatra
   six
@@ -535,5 +522,5 @@ DEPENDENCIES
   therubyracer
   thin
   uglifier (~> 1.3.0)
-  unicorn (~> 4.4.0)
+  unicorn
   webmock
index e910dc9..02b4722 100644 (file)
--- a/README.md
+++ b/README.md
-# Welcome to GitLab! Self hosted Git management software
+## GitLab: self hosted Git management software
 
+![logo](https://raw.github.com/gitlabhq/gitlabhq/master/public/gitlab_logo.png)
 
-## Badges:
+### GitLab allows you to
+ * keep your code secure on your own server
+ * manage repositories, users and access permissions
+ * communicate though issues, line-comments and wiki's
+ * perform code reviews with merge requests
+
+### GitLab is
+
+* powered by Ruby on Rails
+* completely free and open source (MIT license)
+* used by 10.000 organization to keep their code secure
+
+### Code status
+
+* [![build status](http://ci.gitlab.org/projects/1/status?ref=master)](http://ci.gitlab.org/projects/1?ref=master) ci.gitlab.org (master branch)
+
+* [![build status](https://secure.travis-ci.org/gitlabhq/gitlabhq.png)](https://travis-ci.org/gitlabhq/gitlabhq) travis-ci.org (master branch)
+
+* [![Code Climate](https://codeclimate.com/github/gitlabhq/gitlabhq.png)](https://codeclimate.com/github/gitlabhq/gitlabhq)
 
-* master: travis-ci.org [![build status](https://secure.travis-ci.org/gitlabhq/gitlabhq.png)](https://travis-ci.org/gitlabhq/gitlabhq)a
-* master: ci.gitlab.org [![CI](http://ci.gitlab.org/projects/1/status?ref=master)](http://ci.gitlab.org/projects/1?ref=master)
-* [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/gitlabhq/gitlabhq)
 * [![Dependency Status](https://gemnasium.com/gitlabhq/gitlabhq.png)](https://gemnasium.com/gitlabhq/gitlabhq)
 
-GitLab is a free project and repository management application
+### Resources
 
+* GitLab.org community site: [Homepage](http://gitlab.org) [Screenshots](http://gitlab.org/screenshots/) [Blog](http://blog.gitlab.org/) [Demo](http://demo.gitlabhq.com/users/sign_in)
 
-## Application details
+* GitLab.com: [Homepage](http://blog.gitlab.com/) [Hosted pricing](http://blog.gitlab.com/pricing/) [Services](http://blog.gitlab.com/services/) [Blog](http://blog.gitlab.com/blog/)
 
-* powered by Ruby on Rails
-* its completely free and open source
-* distributed under the MIT License
+* GitLab CI: [Readme](https://github.com/gitlabhq/gitlab-ci/blob/master/README.md) of the GitLab open-source continuous integration server
 
-## Requirements
+### Requirements
 
-* Ubuntu/Debian
+* Ubuntu/Debian*
 * ruby 1.9.3+
 * MySQL
 * git
 * gitlab-shell
 * redis
 
-## Install
+* More details are in the [requirements doc](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/requirements.md)
+
+### Installation
+
+You can either follow the "ordinary" Installation guide to install it on a machine or use the Vagrant virtual machine. The Installation guide is recommended to set up a production server. The Vargrant virtual machine is recommended for development since it makes it much easier to set up all the dependencies for integration testing.
+
+* [Installation guide for latest stable release](https://github.com/gitlabhq/gitlabhq/blob/4-2-stable/doc/install/installation.md)
+
+* [Installation guide for the current master branch](https://github.com/gitlabhq/gitlabhq/blob/master/doc/install/installation.md)
+
+* [Vagrant virtual machine](https://github.com/gitlabhq/gitlab-vagrant-vm)
+
+### Starting
+
+1. The Installation guide contains instructions to download an init script and run that on boot. With the init script you can also start GitLab with:
+
+        sudo service gitlab start
+
+  or
+
+        sudo /etc/init.d/gitlab restart
+
+2. Start it with [Foreman](https://github.com/ddollar/foreman) in development model
+
+        bundle exec foreman start -p 3000
+
+3. Start it manually in development mode
+
+        bundle exec rails s
+        bundle exec rake sidekiq:start
+
+### Running the tests
+
+* Seed the database with
+
+        bundle exec rake db:setup RAILS_ENV=test
+        bundle exec rake db:seed_fu RAILS_ENV=test
+
+* Run all tests
+
+        bundle exec rake gitlab:test
+
+* Rspec unit and functional tests
+
+        bundle exec rake spec
+
+* Spinach integration tests
+
+        bundle exec rake spinach
+
+### Getting help
+
+* [Troubleshooting guide](https://github.com/gitlabhq/gitlab-public-wiki/wiki/Trouble-Shooting-Guide)
+
+* [Support forum](https://groups.google.com/forum/#!forum/gitlabhq)
+
+* [Feedback and suggestions forum](http://gitlab.uservoice.com/forums/176466-general)
+
+* [Paid support](http://blog.gitlab.com/support/)
+
+* [Paid services](http://blog.gitlab.com/services/)
+
+### New versions and the API
+
+Each month on the 22th a new version is released together with an upgrade guide.
+
+* [Upgrade guides](https://github.com/gitlabhq/gitlabhq/wiki)
+
+* [Roadmap](https://github.com/gitlabhq/gitlabhq/blob/master/ROADMAP.md)
+
+### Other documentation
+
+* [GitLab API](https://github.com/gitlabhq/gitlabhq/blob/master/doc/api/README.md)
+
+* [Rake tasks](https://github.com/gitlabhq/gitlabhq/tree/master/doc/raketasks)
+
+* [GitLab recipes](https://github.com/gitlabhq/gitlab-recipes)
+
+### Getting in touch
 
-Checkout [wiki](https://github.com/gitlabhq/gitlabhq/wiki) pages for installation information, migration, etc.
+* [Contributing guide](https://github.com/gitlabhq/gitlabhq/blob/master/CONTRIBUTING.md)
 
-## [Community](http://gitlab.org/community/)
+* [Core team](https://github.com/gitlabhq?tab=members)
 
-## [Contact](http://gitlab.org/contact/)
+* [Contributors](https://github.com/gitlabhq/gitlabhq/graphs/contributors)
 
-## Contribute
+* [Leader](https://github.com/randx)
 
-[Developer Guide](https://github.com/gitlabhq/gitlabhq/wiki/Developer-Guide)
-Want to help - send a pull request.
-We'll accept good pull requests.
+* [Contact page](http://gitlab.org/contact/)
index d148b51..bf4fe69 100644 (file)
@@ -4,9 +4,4 @@
 
 * Replace gitolite with gitlab-shell 
 * Usability improvements
-* Notification improvements
-
-### v4.2 February 22
-
-* Teams
-
+* Notification improvements
\ No newline at end of file
diff --git a/app/assets/fonts/OFL.txt b/app/assets/fonts/OFL.txt
deleted file mode 100644 (file)
index 3ce219f..0000000
+++ /dev/null
@@ -1,92 +0,0 @@
-Copyright (c) 2010, Jan Gerner (post@yanone.de)\r
-This Font Software is licensed under the SIL Open Font License, Version 1.1.\r
-This license is copied below, and is also available with a FAQ at:\r
-http://scripts.sil.org/OFL\r
-\r
-\r
------------------------------------------------------------\r
-SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007\r
------------------------------------------------------------\r
-\r
-PREAMBLE\r
-The goals of the Open Font License (OFL) are to stimulate worldwide\r
-development of collaborative font projects, to support the font creation\r
-efforts of academic and linguistic communities, and to provide a free and\r
-open framework in which fonts may be shared and improved in partnership\r
-with others.\r
-\r
-The OFL allows the licensed fonts to be used, studied, modified and\r
-redistributed freely as long as they are not sold by themselves. The\r
-fonts, including any derivative works, can be bundled, embedded, \r
-redistributed and/or sold with any software provided that any reserved\r
-names are not used by derivative works. The fonts and derivatives,\r
-however, cannot be released under any other type of license. The\r
-requirement for fonts to remain under this license does not apply\r
-to any document created using the fonts or their derivatives.\r
-\r
-DEFINITIONS\r
-"Font Software" refers to the set of files released by the Copyright\r
-Holder(s) under this license and clearly marked as such. This may\r
-include source files, build scripts and documentation.\r
-\r
-"Reserved Font Name" refers to any names specified as such after the\r
-copyright statement(s).\r
-\r
-"Original Version" refers to the collection of Font Software components as\r
-distributed by the Copyright Holder(s).\r
-\r
-"Modified Version" refers to any derivative made by adding to, deleting,\r
-or substituting -- in part or in whole -- any of the components of the\r
-Original Version, by changing formats or by porting the Font Software to a\r
-new environment.\r
-\r
-"Author" refers to any designer, engineer, programmer, technical\r
-writer or other person who contributed to the Font Software.\r
-\r
-PERMISSION & CONDITIONS\r
-Permission is hereby granted, free of charge, to any person obtaining\r
-a copy of the Font Software, to use, study, copy, merge, embed, modify,\r
-redistribute, and sell modified and unmodified copies of the Font\r
-Software, subject to the following conditions:\r
-\r
-1) Neither the Font Software nor any of its individual components,\r
-in Original or Modified Versions, may be sold by itself.\r
-\r
-2) Original or Modified Versions of the Font Software may be bundled,\r
-redistributed and/or sold with any software, provided that each copy\r
-contains the above copyright notice and this license. These can be\r
-included either as stand-alone text files, human-readable headers or\r
-in the appropriate machine-readable metadata fields within text or\r
-binary files as long as those fields can be easily viewed by the user.\r
-\r
-3) No Modified Version of the Font Software may use the Reserved Font\r
-Name(s) unless explicit written permission is granted by the corresponding\r
-Copyright Holder. This restriction only applies to the primary font name as\r
-presented to the users.\r
-\r
-4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font\r
-Software shall not be used to promote, endorse or advertise any\r
-Modified Version, except to acknowledge the contribution(s) of the\r
-Copyright Holder(s) and the Author(s) or with their explicit written\r
-permission.\r
-\r
-5) The Font Software, modified or unmodified, in part or in whole,\r
-must be distributed entirely under this license, and must not be\r
-distributed under any other license. The requirement for fonts to\r
-remain under this license does not apply to any document created\r
-using the Font Software.\r
-\r
-TERMINATION\r
-This license becomes null and void if any of the above conditions are\r
-not met.\r
-\r
-DISCLAIMER\r
-THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF\r
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT\r
-OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE\r
-COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\r
-INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL\r
-DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\r
-FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM\r
-OTHER DEALINGS IN THE FONT SOFTWARE.\r
diff --git a/app/assets/fonts/YanoneKaffeesatz-Light.ttf b/app/assets/fonts/YanoneKaffeesatz-Light.ttf
deleted file mode 100644 (file)
index 5026d3b..0000000
Binary files a/app/assets/fonts/YanoneKaffeesatz-Light.ttf and /dev/null differ
similarity index 89%
rename from vendor/assets/javascripts/branch-graph.js
rename to app/assets/javascripts/branch-graph.js
index fb22953..137e87d 100644 (file)
             });
 
           } else if (c.space < this.commits[i].space) {
-            r.path([
-                "M", x - 5, y,
-                "l-5-2,0,4,5,-2",
-                "L", x - 10, y,
-                "L", x - 15, psy,
-                "L", cx + 5, psy,
-                "L", cx, cy])
-            .attr({
-              stroke: this.colors[this.commits[i].space], 
-              "stroke-width": 2
-            });
+            if (y == psy) {
+                r.path([
+                    "M", x - 5, y,
+                    "l-5,-2,0,4,5,-2",
+                    "L", x - 10, y,
+                    "L", x - 15, psy,
+                    "L", cx + 5, psy,
+                    "L", cx, cy])
+                .attr({
+                  stroke: this.colors[this.commits[i].space], 
+                  "stroke-width": 2
+                });
+            } else {
+                r.path([
+                    "M", x - 3, y - 6,
+                    "l-4,-3,4,-2,0,5",
+                    "L", x - 5, y - 10,
+                    "L", x - 10, psy,
+                    "L", cx + 5, psy,
+                    "L", cx, cy])
+                .attr({
+                  stroke: this.colors[this.commits[i].space], 
+                  "stroke-width": 2
+                });
+            }
           } else {
             r.path([
                 "M", x - 3, y + 6,
   
 }(this);
 Raphael.fn.commitTooltip = function(x, y, commit){
-  var nameText, idText, messageText
+  var icon, nameText, idText, messageText
     , boxWidth = 300
     , boxHeight = 200;
   
-  nameText = this.text(x, y + 10, commit.author.name);
+  icon = this.image(commit.author.icon, x, y, 20, 20);
+  nameText = this.text(x + 25, y + 10, commit.author.name);
   idText = this.text(x, y + 35, commit.id);
   messageText = this.text(x, y + 50, commit.message);
   
-  textSet = this.set(nameText, idText, messageText).attr({
+  textSet = this.set(icon, nameText, idText, messageText).attr({
     "text-anchor": "start",
     "font": "12px Monaco, monospace"
   });
index d789f54..d707657 100644 (file)
@@ -54,10 +54,10 @@ $ ->
     $(@).parents('form').submit()
 
   # Flash
-  if (flash = $("#flash-container")).length > 0
-    flash.click -> $(@).slideUp("slow")
-    flash.slideDown "slow"
-    setTimeout (-> flash.slideUp("slow")), 3000
+  if (flash = $(".flash-container")).length > 0
+    flash.click -> $(@).fadeOut()
+    flash.show()
+    setTimeout (-> flash.fadeOut()), 3000
 
   # Disable form buttons while a form is submitting
   $('body').on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) ->
index d03a487..24106c6 100644 (file)
@@ -18,3 +18,18 @@ $ ->
   # Ref switcher
   $('.project-refs-select').on 'change', ->
     $(@).parents('form').submit()
+
+  $('#project_issues_enabled').change ->
+    if ($(this).is(':checked') == true)
+      $('#project_issues_tracker').removeAttr('disabled')
+    else
+      $('#project_issues_tracker').attr('disabled', 'disabled')
+
+    $('#project_issues_tracker').change()
+
+  $('#project_issues_tracker').change ->
+    if ($(this).val() == gon.default_issues_tracker || $(this).is(':disabled'))
+      $('#project_issues_tracker_id').attr('disabled', 'disabled')
+    else
+      $('#project_issues_tracker_id').removeAttr('disabled')
+
index 7ac8c2d..c967c2d 100644 (file)
@@ -67,27 +67,17 @@ table a code {
 }
 
 /** FLASH message **/
-#flash-container {
-  height: 50px;
-  position: fixed;
-  z-index: 10001;
-  top: 0px;
-  width: 100%;
-  margin-bottom: 15px;
-  overflow: hidden;
-  background: white;
-  cursor: pointer;
-  border-bottom: 1px solid #ccc;
-  text-align: center;
+.flash-container {
   display: none;
+  .alert {
+    cursor: pointer;
+    margin: 0;
+    text-align: center;
+    border-radius: 0;
 
-  h4 {
-    color: #666;
-    font-size: 18px;
-    line-height: 38px;
-    padding-top: 5px;
-    margin: 2px;
-    font-weight: normal;
+    span {
+      font-size: 14px;
+    }
   }
 }
 
@@ -203,10 +193,6 @@ input[type=text] {
   }
 }
 
-input.git_clone_url {
-  width: 325px;
-}
-
 .merge-request-form-holder {
   select {
     width: 300px;
index dcfd610..9e015eb 100644 (file)
@@ -30,6 +30,8 @@
   border-color: #DDD;
 }
 
+.well { padding: 15px; }
+
 /** HELPERS **/
 .nothing_here_message {
   text-align: center;
index a0c9a6c..8cc9986 100644 (file)
@@ -1,7 +1,2 @@
-@font-face{
-  font-family: Yanone;
-  src: font-url('YanoneKaffeesatz-Light.ttf');
-}
-
 /** Typo **/
 $monospace_font: 'Menlo', 'Liberation Mono', 'Consolas', 'Courier New', 'andale mono', 'lucida console', monospace;
index f416be9..1e5fff6 100644 (file)
 @mixin header-font {
   color: $style_color;
   text-shadow: 0 1px 1px #FFF;
-  font-family: 'Yanone', sans-serif;
-  font-size: 24px;
-  line-height: 36px;
+  font-size: 18px;
+  line-height: 42px;
   font-weight: normal;
+  letter-spacing: -1px;
+}
+
+@mixin md-typography {
+  code { padding: 0 4px; }
+  p { font-size: 13px; }
+  h1 { font-size: 26px; line-height: 40px; margin: 10px 0;}
+  h2 { font-size: 22px; line-height: 40px; margin: 10px 0;}
+  h3 { font-size: 18px; line-height: 40px; margin: 10px 0;}
+  h4 { font-size: 16px; line-height: 20px; margin: 10px 0;}
+  h5 { font-size: 14px; line-height: 20px; margin: 10px 0;}
+  h6 { font-size: 12px; line-height: 20px; margin: 10px 0;}
 }
index 781577c..1f0c480 100644 (file)
@@ -87,16 +87,15 @@ a:focus {
  *
  */
 .wiki {
+  @include md-typography;
+
   font-size: 13px;
+  line-height: 20px;
 
-  code { padding: 0 4px; }
-  p { font-size: 13px; }
-  h1 { font-size: 32px; line-height: 40px; margin: 10px 0;}
-  h2 { font-size: 26px; line-height: 40px; margin: 10px 0;}
-  h3 { font-size: 22px; line-height: 40px; margin: 10px 0;}
-  h4 { font-size: 18px; line-height: 20px; margin: 10px 0;}
-  h5 { font-size: 14px; line-height: 20px; margin: 10px 0;}
-  h6 { font-size: 12px; line-height: 20px; margin: 10px 0;}
   .white .highlight pre { background: #f5f5f5; }
   ul { margin: 0 0 9px 25px !important; }
 }
+
+.md {
+  @include md-typography;
+}
index 6018ff7..4196ea7 100644 (file)
@@ -1,8 +1,7 @@
 .black .highlight {
-  background-color: #333;
   pre {
+    background-color: #333;
     color: #eee;
-    background: inherit;
   }
 
   .hll { display: block; background-color: darken($hover, 65%) }
index a389a9b..0df3929 100644 (file)
       }
     }
     .line_content {
+      display: block;
       white-space: pre;
-      height: 14px;
+      height: 18px;
       margin: 0px;
       padding: 0px;
       border: none;
index df8fd8d..94e1d0b 100644 (file)
       color: #666;
     }
     .event-note {
-      padding-top: 5px;
-      padding-left: 5px;
-      display: inline-block;
       color: #555;
+      margin-top: 5px;
+      margin-left: 40px;
 
       .note-file-attach {
-        margin-left: -25px;
-        float: left;
         .note-image-attach {
+          margin-top: 4px;
           margin-left: 0px;
           max-width: 200px;
         }
@@ -66,8 +64,8 @@
       color: #777;
       float: left;
       font-size: 16px;
-      line-height: 18px;
-      margin: 5px;
+      line-height: 16px;
+      margin-right: 5px;
     }
   }
   .avatar {
index 99c8275..6456047 100644 (file)
@@ -67,7 +67,7 @@ header {
     position: relative;
     float: left;
     margin: 0;
-    margin-left: 15px;
+    margin-left: 10px;
     @include header-font;
   }
 
index 89b8f1c..e3fe0b4 100644 (file)
@@ -1,7 +1,7 @@
 /* Login Page */
 body.login-page{
-  padding-top: 7%;
-  background: #666;
+  background: #EEE;
+  .container .content { padding-top: 5%; }
 }
 
 .login-box{
index 1f92a3a..1b4280f 100644 (file)
@@ -83,6 +83,7 @@ ul.notes {
       margin-top: -20px;
     }
     .note-body {
+      @include md-typography;
       margin-left: 45px;
     }
     .note-header {
index b37830b..ada0780 100644 (file)
@@ -80,6 +80,7 @@
     border: 1px solid #BBB;
     box-shadow: none;
     margin-left: -1px;
+    background: #FFF;
   }
 }
 
index 0cc73f9..0765b30 100644 (file)
@@ -7,12 +7,13 @@ class IssuesListContext < BaseContext
     @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.opened.assigned(current_user)
+              when issues_filter[:to_me] then @project.issues.assigned(current_user)
+              when issues_filter[:by_me] then @project.issues.authored(current_user)
               else @project.issues.opened
               end
 
     @issues = @issues.tagged_with(params[:label_name]) if params[:label_name].present?
-    @issues = @issues.includes(:author, :project).order("updated_at")
+    @issues = @issues.includes(:author, :project)
 
     # Filter by specific assignee_id (or lack thereof)?
     if params[:assignee_id].present?
index e7dbcad..e646987 100644 (file)
@@ -1,7 +1,7 @@
 class Admin::Teams::MembersController < Admin::Teams::ApplicationController
   def new
     @users = User.potential_team_members(user_team)
-    @users = UserDecorator.decorate @users
+    @users = UserDecorator.decorate_collection @users
   end
 
   def create
index 2e7114e..43e6f09 100644 (file)
@@ -45,7 +45,7 @@ class Admin::UsersController < Admin::ApplicationController
   end
 
   def unblock
-    if admin_user.update_attribute(:blocked, false)
+    if admin_user.activate
       redirect_to :back, alert: "Successfully unblocked"
     else
       redirect_to :back, alert: "Error occured. User was not unblocked"
index 1f211ba..6b72f32 100644 (file)
@@ -5,6 +5,7 @@ class ApplicationController < ActionController::Base
   before_filter :add_abilities
   before_filter :dev_tools if Rails.env == 'development'
   before_filter :default_headers
+  before_filter :add_gon_variables
 
   protect_from_forgery
 
@@ -29,7 +30,7 @@ class ApplicationController < ActionController::Base
   end
 
   def reject_blocked!
-    if current_user && current_user.blocked
+    if current_user && current_user.blocked?
       sign_out current_user
       flash[:alert] = "Your account is blocked. Retry when an admin unblock it."
       redirect_to new_user_session_path
@@ -37,7 +38,7 @@ class ApplicationController < ActionController::Base
   end
 
   def after_sign_in_path_for resource
-    if resource.is_a?(User) && resource.respond_to?(:blocked) && resource.blocked
+    if resource.is_a?(User) && resource.respond_to?(:blocked?) && resource.blocked?
       sign_out resource
       flash[:alert] = "Your account is blocked. Retry when an admin unblock it."
       new_user_session_path
@@ -148,4 +149,8 @@ class ApplicationController < ActionController::Base
     headers['X-Frame-Options'] = 'DENY'
     headers['X-XSS-Protection'] = '1; mode=block'
   end
+
+  def add_gon_variables
+    gon.default_issues_tracker = Project.issues_tracker.default_value
+  end
 end
index 534ae1e..9dc0d96 100644 (file)
@@ -13,7 +13,7 @@ class CommitsController < ProjectResourceController
     @limit, @offset = (params[:limit] || 40), (params[:offset] || 0)
 
     @commits = @repo.commits(@ref, @path, @limit, @offset)
-    @commits = CommitDecorator.decorate(@commits)
+    @commits = CommitDecorator.decorate_collection(@commits)
 
     respond_to do |format|
       format.html # index.html.erb
index ae20f9c..bd3f111 100644 (file)
@@ -16,7 +16,7 @@ class CompareController < ProjectResourceController
     @refs_are_same = result[:same]
     @line_notes    = []
 
-    @commits = CommitDecorator.decorate(@commits)
+    @commits = CommitDecorator.decorate_collection(@commits)
   end
 
   def create
index c370433..33cb2d2 100644 (file)
@@ -1,5 +1,6 @@
 class GraphController < ProjectResourceController
   include ExtractsPath
+  include ApplicationHelper
 
   # Authorize
   before_filter :authorize_read_project!
@@ -20,7 +21,10 @@ class GraphController < ProjectResourceController
     respond_to do |format|
       format.html
       format.json do
-        graph = Gitlab::Graph::JsonBuilder.new(project, @ref, @commit)
+        graph = Graph::JsonBuilder.new(project, @ref, @commit)
+        graph.commits.each do |c|
+          c.icon = gravatar_icon(c.author.email)
+        end
         render :json => graph.to_json
       end
     end
index 67f9617..788f2c3 100644 (file)
@@ -81,7 +81,8 @@ class MergeRequestsController < ProjectResourceController
   end
 
   def automerge
-    return access_denied! unless can?(current_user, :accept_mr, @project)
+    return access_denied! unless allowed_to_merge?
+
     if @merge_request.opened? && @merge_request.can_be_merged?
       @merge_request.should_remove_source_branch = params[:should_remove_source_branch]
       @merge_request.automerge!(current_user)
@@ -142,6 +143,19 @@ class MergeRequestsController < ProjectResourceController
     # Get commits from repository
     # or from cache if already merged
     @commits = @merge_request.commits
-    @commits = CommitDecorator.decorate(@commits)
+    @commits = CommitDecorator.decorate_collection(@commits)
+
+    @allowed_to_merge = allowed_to_merge?
+    @show_merge_controls = @merge_request.opened? && @commits.any? && @allowed_to_merge
+  end
+
+  def allowed_to_merge?
+    action = if project.protected_branch?(@merge_request.target_branch)
+               :push_code_to_protected_branches
+             else
+               :push_code
+             end
+
+    can?(current_user, action, @project)
   end
 end
index 57f1e9e..cdac28c 100644 (file)
@@ -32,7 +32,7 @@ class MilestonesController < ProjectResourceController
 
   def show
     @issues = @milestone.issues
-    @users = UserDecorator.decorate(@milestone.participants)
+    @users = UserDecorator.decorate_collection(@milestone.participants)
     @merge_requests = @milestone.merge_requests
 
     respond_to do |format|
index 5da3fbf..f703cf6 100644 (file)
@@ -1,5 +1,3 @@
-require Rails.root.join('lib', 'gitlab', 'graph', 'json_builder')
-
 class ProjectsController < ProjectResourceController
   skip_before_filter :project, only: [:new, :create]
   skip_before_filter :repository, only: [:new, :create]
index ead62e1..4bd70fd 100644 (file)
@@ -8,7 +8,7 @@ class Teams::MembersController < Teams::ApplicationController
 
   def new
     @users = User.potential_team_members(user_team)
-    @users = UserDecorator.decorate @users
+    @users = UserDecorator.decorate_collection @users
   end
 
   def create
index 3023699..b805b34 100644 (file)
@@ -1,27 +1,28 @@
-class ApplicationDecorator < Draper::Base
+class ApplicationDecorator < Draper::Decorator
+  delegate_all
   # Lazy Helpers
   #   PRO: Call Rails helpers without the h. proxy
   #        ex: number_to_currency(model.price)
   #   CON: Add a bazillion methods into your decorator's namespace
   #        and probably sacrifice performance/memory
-  #  
+  #
   #   Enable them by uncommenting this line:
   #   lazy_helpers
 
   # Shared Decorations
   #   Consider defining shared methods common to all your models.
-  #   
+  #
   #   Example: standardize the formatting of timestamps
   #
   #   def formatted_timestamp(time)
-  #     h.content_tag :span, time.strftime("%a %m/%d/%y"), 
-  #                   class: 'timestamp' 
+  #     h.content_tag :span, time.strftime("%a %m/%d/%y"),
+  #                   class: 'timestamp'
   #   end
-  # 
+  #
   #   def created_at
   #     formatted_timestamp(model.created_at)
   #   end
-  # 
+  #
   #   def updated_at
   #     formatted_timestamp(model.updated_at)
   #   end
index dad2347..955dbc1 100644 (file)
@@ -164,7 +164,8 @@ module ApplicationHelper
   end
 
   def image_url(source)
-    root_url + path_to_image(source)
+    # prevent relative_root_path being added twice (it's part of root_url and path_to_image)
+    root_url.sub(/#{root_path}$/, path_to_image(source))
   end
 
   alias_method :url_to_image, :image_url
index ed7e3e8..5438511 100644 (file)
@@ -27,6 +27,7 @@ module IssuesHelper
       all: "all",
       closed: "closed",
       to_me: "assigned-to-me",
+      by_me: "created-by-me",
       open: "open"
     }
   end
@@ -40,4 +41,39 @@ module IssuesHelper
   def issues_active_milestones
     @project.milestones.active.order("id desc").all
   end
+
+  def url_for_project_issues
+    return "" if @project.nil?
+
+    if @project.used_default_issues_tracker?
+      project_issues_filter_path(@project)
+    else
+      url = Settings[:issues_tracker][@project.issues_tracker]["project_url"]
+      url.gsub(':project_id', @project.id.to_s)
+         .gsub(':issues_tracker_id', @project.issues_tracker_id.to_s)
+    end
+  end
+
+  def url_for_issue(issue_id)
+    return "" if @project.nil?
+
+    if @project.used_default_issues_tracker?
+      url = project_issue_url project_id: @project, id: issue_id
+    else
+      url = Settings[:issues_tracker][@project.issues_tracker]["issues_url"]
+      url.gsub(':id', issue_id.to_s)
+        .gsub(':project_id', @project.id.to_s)
+        .gsub(':issues_tracker_id', @project.issues_tracker_id.to_s)
+    end
+  end
+
+  def title_for_issue(issue_id)
+    return "" if @project.nil?
+
+    if @project.used_default_issues_tracker? && issue = @project.issues.where(id: issue_id).first
+      issue.title
+    else
+      ""
+    end
+  end
 end
index 0f2b695..fab0085 100644 (file)
@@ -13,13 +13,15 @@ module TreeHelper
     tree += render partial: 'tree/tree_item', collection: folders, locals: {type: 'folder'} if folders.present?
 
     files.each do |f|
-      if f.respond_to?(:url)
-        # Object is a Submodule
-        tree += render partial: 'tree/submodule_item', object: f
-      else
-        # Object is a Blob
-        tree += render partial: 'tree/tree_item', object: f, locals: {type: 'file'}
-      end
+      html = if f.respond_to?(:url)
+               # Object is a Submodule
+               render partial: 'tree/submodule_item', object: f
+             else
+               # Object is a Blob
+               render partial: 'tree/tree_item', object: f, locals: {type: 'file'}
+             end
+
+      tree += html if html.present?
     end
 
     tree.html_safe
index 6fda2e5..41f7127 100644 (file)
@@ -91,7 +91,6 @@ class Ability
         :admin_team_member,
         :admin_merge_request,
         :admin_note,
-        :accept_mr,
         :admin_wiki,
         :admin_project
       ]
diff --git a/app/models/graph/commit.rb b/app/models/graph/commit.rb
new file mode 100644 (file)
index 0000000..8ed61f4
--- /dev/null
@@ -0,0 +1,59 @@
+require "grit"
+
+module Graph
+  class Commit
+    include ActionView::Helpers::TagHelper
+
+    attr_accessor :time, :spaces, :refs, :parent_spaces, :icon
+
+    def initialize(commit)
+      @_commit = commit
+      @time = -1
+      @spaces = []
+      @parent_spaces = []
+    end
+
+    def method_missing(m, *args, &block)
+      @_commit.send(m, *args, &block)
+    end
+
+    def to_graph_hash
+      h = {}
+      h[:parents] = self.parents.collect do |p|
+        [p.id,0,0]
+      end
+      h[:author]  = {
+        name: author.name,
+        email: author.email,
+        icon: icon
+      }
+      h[:time]    = time
+      h[:space]   = spaces.first
+      h[:parent_spaces]   = parent_spaces
+      h[:refs]    = refs.collect{|r|r.name}.join(" ") unless refs.nil?
+      h[:id]      = sha
+      h[:date]    = date
+      h[:message] = message
+      h
+    end
+
+    def add_refs(ref_cache, repo)
+      if ref_cache.empty?
+        repo.refs.each do |ref|
+          ref_cache[ref.commit.id] ||= []
+          ref_cache[ref.commit.id] << ref
+        end
+      end
+      @refs = ref_cache[@_commit.id] if ref_cache.include?(@_commit.id)
+      @refs ||= []
+    end
+
+    def space
+      if @spaces.size > 0
+        @spaces.first
+      else
+        0
+      end
+    end
+  end
+end
diff --git a/app/models/graph/json_builder.rb b/app/models/graph/json_builder.rb
new file mode 100644 (file)
index 0000000..013d15f
--- /dev/null
@@ -0,0 +1,291 @@
+require "grit"
+
+module Graph
+  class JsonBuilder
+    attr_accessor :days, :commits, :ref_cache, :repo
+
+    def self.max_count
+      @max_count ||= 650
+    end
+
+    def initialize project, ref, commit
+      @project = project
+      @ref = ref
+      @commit = commit
+      @repo = project.repo
+      @ref_cache = {}
+
+      @commits = collect_commits
+      @days = index_commits
+    end
+
+    def to_json(*args)
+      {
+        days: @days.compact.map { |d| [d.day, d.strftime("%b")] },
+        commits: @commits.map(&:to_graph_hash)
+      }.to_json(*args)
+    end
+
+  protected
+
+    # Get commits from repository
+    #
+    def collect_commits
+
+      @commits = Grit::Commit.find_all(repo, nil, {date_order: true, max_count: self.class.max_count, skip: to_commit}).dup
+
+      # Decorate with app/models/commit.rb
+      @commits.map! { |commit| Commit.new(commit) }
+
+      # Decorate with lib/gitlab/graph/commit.rb
+      @commits.map! { |commit| Graph::Commit.new(commit) }
+
+      # add refs to each commit
+      @commits.each { |commit| commit.add_refs(ref_cache, repo) }
+
+      @commits
+    end
+
+    # Method is adding time and space on the
+    # list of commits. As well as returns date list
+    # corelated with time set on commits.
+    #
+    # @param [Array<Graph::Commit>] commits to index
+    #
+    # @return [Array<TimeDate>] list of commit dates corelated with time on commits
+    def index_commits
+      days, times = [], []
+      map = {}
+
+      commits.reverse.each_with_index do |c,i|
+        c.time = i
+        days[i] = c.committed_date
+        map[c.id] = c
+        times[i] = c
+      end
+
+      @_reserved = {}
+      days.each_index do |i|
+        @_reserved[i] = []
+      end
+
+      commits_sort_by_ref.each do |commit|
+        if map.include? commit.id then
+          place_chain(map[commit.id], map)
+        end
+      end
+
+      # find parent spaces for not overlap lines
+      times.each do |c|
+        c.parent_spaces.concat(find_free_parent_spaces(c, map, times))
+      end
+
+      days
+    end
+
+    # Skip count that the target commit is displayed in center.
+    def to_commit
+      commits = Grit::Commit.find_all(repo, nil, {date_order: true})
+      commit_index = commits.index do |c|
+        c.id == @commit.id
+      end
+
+      if commit_index && (self.class.max_count / 2 < commit_index) then
+        # get max index that commit is displayed in the center.
+        commit_index - self.class.max_count / 2
+      else
+        0
+      end
+    end
+
+    def commits_sort_by_ref
+      commits.sort do |a,b|
+        if include_ref?(a)
+          -1
+        elsif include_ref?(b)
+          1
+        else
+          b.committed_date <=> a.committed_date
+        end
+      end
+    end
+
+    def include_ref?(commit)
+      heads = commit.refs.select do |ref|
+        ref.is_a?(Grit::Head) or ref.is_a?(Grit::Remote) or ref.is_a?(Grit::Tag)
+      end
+
+      heads.map! do |head|
+        head.name
+      end
+
+      heads.include?(@ref)
+    end
+
+    def find_free_parent_spaces(commit, map, times)
+      spaces = []
+
+      commit.parents.each do |p|
+        if map.include?(p.id) then
+          parent = map[p.id]
+
+          range = if commit.time < parent.time then
+                    commit.time..parent.time
+                  else
+                    parent.time..commit.time
+                  end
+
+          space = if commit.space >= parent.space then
+                    find_free_parent_space(range, parent.space, -1, commit.space, times)
+                  else
+                    find_free_parent_space(range, commit.space, -1, parent.space, times)
+                  end
+
+          mark_reserved(range, space)
+          spaces << space
+        end
+      end
+
+      spaces
+    end
+
+    def find_free_parent_space(range, space_base, space_step, space_default, times)
+      if is_overlap?(range, times, space_default) then
+        find_free_space(range, space_step, space_base, space_default)
+      else
+        space_default
+      end
+    end
+
+    def is_overlap?(range, times, overlap_space)
+      range.each do |i|
+        if i != range.first &&
+          i != range.last &&
+          times[i].spaces.include?(overlap_space) then
+
+          return true;
+        end
+      end
+
+      false
+    end
+
+    # Add space mark on commit and its parents
+    #
+    # @param [Graph::Commit] the commit object.
+    # @param [Hash<String,Graph::Commit>] map of commits
+    def place_chain(commit, map, parent_time = nil)
+      leaves = take_left_leaves(commit, map)
+      if leaves.empty?
+        return
+      end
+
+      time_range = leaves.last.time..leaves.first.time
+      space_base = get_space_base(leaves, map)
+      space = find_free_space(time_range, 2, space_base)
+      leaves.each do |l|
+        l.spaces << space
+        # Also add space to parent
+        l.parents.each do |p|
+          if map.include?(p.id)
+            parent = map[p.id]
+            if parent.space > 0
+              parent.spaces << space
+            end
+          end
+        end
+      end
+
+      # and mark it as reserved
+      min_time = leaves.last.time
+      parents = leaves.last.parents.collect
+      parents.each do |p|
+        if map.include? p.id
+          parent = map[p.id]
+          if parent.time < min_time
+            min_time = parent.time
+          end
+        end
+      end
+
+      if parent_time.nil?
+        max_time = leaves.first.time
+      else
+        max_time = parent_time - 1
+      end
+      mark_reserved(min_time..max_time, space)
+
+      # Visit branching chains
+      leaves.each do |l|
+        parents = l.parents.collect.select{|p| map.include? p.id and map[p.id].space.zero?}
+        for p in parents
+          place_chain(map[p.id], map, l.time)
+        end
+      end
+    end
+
+    def get_space_base(leaves, map)
+      space_base = 1
+      if leaves.last.parents.size > 0
+        first_parent = leaves.last.parents.first
+        if map.include?(first_parent.id)
+          first_p = map[first_parent.id]
+          if first_p.space > 0
+            space_base = first_p.space
+          end
+        end
+      end
+      space_base
+    end
+
+    def mark_reserved(time_range, space)
+      for day in time_range
+        @_reserved[day].push(space)
+      end
+    end
+
+    def find_free_space(time_range, space_step, space_base = 1, space_default = nil)
+      space_default ||= space_base
+
+      reserved = []
+      for day in time_range
+        reserved += @_reserved[day]
+      end
+      reserved.uniq!
+
+      space = space_default
+      while reserved.include?(space) do
+        space += space_step
+        if space < space_base then
+          space_step *= -1
+          space = space_base + space_step
+        end
+      end
+
+      space
+    end
+
+    # Takes most left subtree branch of commits
+    # which don't have space mark yet.
+    #
+    # @param [Graph::Commit] the commit object.
+    # @param [Hash<String,Graph::Commit>] map of commits
+    #
+    # @return [Array<Graph::Commit>] list of branch commits
+    def take_left_leaves(commit, map)
+      leaves = []
+      leaves.push(commit) if commit.space.zero?
+
+      while true
+        return leaves if commit.parents.count.zero?
+        return leaves unless map.include? commit.parents.first.id
+
+        commit = map[commit.parents.first.id]
+
+        return leaves unless commit.space.zero?
+
+        leaves.push(commit)
+      end
+    end
+  end
+end
index 8ba9298..7651ce2 100644 (file)
@@ -2,13 +2,14 @@
 #
 # Table name: namespaces
 #
-#  id         :integer          not null, primary key
-#  name       :string(255)      not null
-#  path       :string(255)      not null
-#  owner_id   :integer          not null
-#  created_at :datetime         not null
-#  updated_at :datetime         not null
-#  type       :string(255)
+#  id          :integer          not null, primary key
+#  name        :string(255)      not null
+#  description :string(255)      not null
+#  path        :string(255)      not null
+#  owner_id    :integer          not null
+#  created_at  :datetime         not null
+#  updated_at  :datetime         not null
+#  type        :string(255)
 #
 
 class Group < Namespace
index 112f43c..f01cad0 100644 (file)
@@ -30,6 +30,10 @@ class Issue < ActiveRecord::Base
       where('assignee_id = :user', user: user.id)
     end
 
+    def authored(user)
+      where('author_id = :user', user: user.id)
+    end
+
     def open_for(user)
       opened.assigned(user)
     end
index 385fa29..c6b3e94 100644 (file)
@@ -2,17 +2,18 @@
 #
 # Table name: namespaces
 #
-#  id         :integer          not null, primary key
-#  name       :string(255)      not null
-#  path       :string(255)      not null
-#  owner_id   :integer          not null
-#  created_at :datetime         not null
-#  updated_at :datetime         not null
-#  type       :string(255)
+#  id          :integer          not null, primary key
+#  name        :string(255)      not null
+#  description :string(255)      not null
+#  path        :string(255)      not null
+#  owner_id    :integer          not null
+#  created_at  :datetime         not null
+#  updated_at  :datetime         not null
+#  type        :string(255)
 #
 
 class Namespace < ActiveRecord::Base
-  attr_accessible :name, :path
+  attr_accessible :name, :description, :path
 
   has_many :projects, dependent: :destroy
   belongs_to :owner, class_name: "User"
@@ -22,7 +23,7 @@ class Namespace < ActiveRecord::Base
             length: { within: 0..255 },
             format: { with: Gitlab::Regex.name_regex,
                       message: "only letters, digits, spaces & '_' '-' '.' allowed." }
-
+  validates :description, length: { within: 0..255 }
   validates :path, uniqueness: true, presence: true, length: { within: 1..255 },
             format: { with: Gitlab::Regex.path_regex,
                       message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
index 7587ef1..07ba7fc 100644 (file)
@@ -11,6 +11,7 @@
 #  creator_id             :integer
 #  default_branch         :string(255)
 #  issues_enabled         :boolean          default(TRUE), not null
+#  issues_tracker         :string           not null
 #  wall_enabled           :boolean          default(TRUE), not null
 #  merge_requests_enabled :boolean          default(TRUE), not null
 #  wiki_enabled           :boolean          default(TRUE), not null
@@ -22,11 +23,12 @@ require "grit"
 
 class Project < ActiveRecord::Base
   include Gitolited
+  extend Enumerize
 
   class TransferError < StandardError; end
 
-  attr_accessible :name, :path, :description, :default_branch,
-    :issues_enabled, :wall_enabled, :merge_requests_enabled,
+  attr_accessible :name, :path, :description, :default_branch, :issues_tracker,
+    :issues_enabled, :wall_enabled, :merge_requests_enabled, :issues_tracker_id,
     :wiki_enabled, :public, :import_url, as: [:default, :admin]
 
   attr_accessible :namespace_id, :creator_id, as: :admin
@@ -43,7 +45,7 @@ class Project < ActiveRecord::Base
 
   has_many :events,             dependent: :destroy
   has_many :merge_requests,     dependent: :destroy
-  has_many :issues,             dependent: :destroy, order: "state, created_at DESC"
+  has_many :issues,             dependent: :destroy, order: "state DESC, created_at DESC"
   has_many :milestones,         dependent: :destroy
   has_many :users_projects,     dependent: :destroy
   has_many :notes,              dependent: :destroy
@@ -72,6 +74,7 @@ class Project < ActiveRecord::Base
                       message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
   validates :issues_enabled, :wall_enabled, :merge_requests_enabled,
             :wiki_enabled, inclusion: { in: [true, false] }
+  validates :issues_tracker_id, length: { within: 0..255 }
 
   validates_uniqueness_of :name, scope: :namespace_id
   validates_uniqueness_of :path, scope: :namespace_id
@@ -93,6 +96,8 @@ class Project < ActiveRecord::Base
   scope :joined, ->(user) { where("namespace_id != ?", user.namespace_id) }
   scope :public_only, -> { where(public: true) }
 
+  enumerize :issues_tracker, :in => (Gitlab.config.issues_tracker.keys).append(:gitlab), :default => :gitlab
+
   class << self
     def abandoned
       project_ids = Event.select('max(created_at) as latest_date, project_id').
@@ -201,6 +206,22 @@ class Project < ActiveRecord::Base
     issues.tag_counts_on(:labels)
   end
 
+  def issue_exists?(issue_id)
+    if used_default_issues_tracker?
+      self.issues.where(id: issue_id).first.present?
+    else
+      true
+    end
+  end
+
+  def used_default_issues_tracker?
+    self.issues_tracker == Project.issues_tracker.default_value
+  end
+
+  def can_have_issues_tracker_id?
+    self.issues_enabled && !self.used_default_issues_tracker?
+  end
+
   def services
     [gitlab_ci_service].compact
   end
index a5ca553..3feb318 100644 (file)
@@ -137,7 +137,7 @@ class Repository
     file_path = File.join(storage_path, self.path_with_namespace, file_name)
 
     # Put files into a directory before archiving
-    prefix = self.path_with_namespace + "/"
+    prefix = File.basename(self.path_with_namespace) + "/"
 
     # Create file if not exists
     unless File.exists?(file_path)
index cd0754d..a8c39f5 100644 (file)
@@ -25,7 +25,7 @@
 #  dark_scheme            :boolean          default(FALSE), not null
 #  theme_id               :integer          default(1), not null
 #  bio                    :string(255)
-#  blocked                :boolean          default(FALSE), not null
+#  state                  :string(255)
 #  failed_attempts        :integer          default(0)
 #  locked_at              :datetime
 #  extern_uid             :string(255)
@@ -46,10 +46,35 @@ class User < ActiveRecord::Base
 
   attr_accessor :force_random_password
 
+  #
+  # Relations
+  #
+
   # Namespace for personal projects
-  has_one :namespace,                 dependent: :destroy, foreign_key: :owner_id,    class_name: "Namespace", conditions: 'type IS NULL'
+  has_one :namespace,
+    dependent: :destroy,
+    foreign_key: :owner_id,
+    class_name: "Namespace",
+    conditions: 'type IS NULL'
+
+  # Profile
+  has_many :keys, dependent: :destroy
+
+  # Groups
+  has_many :groups, class_name: "Group", foreign_key: :owner_id
+
+  # Teams
+  has_many :own_teams,
+    class_name: "UserTeam",
+    foreign_key: :owner_id,
+    dependent: :destroy
+
+  has_many :user_team_user_relationships, dependent: :destroy
+  has_many :user_teams, through: :user_team_user_relationships
+  has_many :user_team_project_relationships, through: :user_teams
+  has_many :team_projects, through: :user_team_project_relationships
 
-  has_many :keys,                     dependent: :destroy
+  # Projects
   has_many :users_projects,           dependent: :destroy
   has_many :issues,                   dependent: :destroy, foreign_key: :author_id
   has_many :notes,                    dependent: :destroy, foreign_key: :author_id
@@ -57,18 +82,16 @@ class User < ActiveRecord::Base
   has_many :events,                   dependent: :destroy, foreign_key: :author_id,   class_name: "Event"
   has_many :assigned_issues,          dependent: :destroy, foreign_key: :assignee_id, class_name: "Issue"
   has_many :assigned_merge_requests,  dependent: :destroy, foreign_key: :assignee_id, class_name: "MergeRequest"
+  has_many :projects, through: :users_projects
 
-  has_many :groups,         class_name: "Group", foreign_key: :owner_id
-  has_many :recent_events,  class_name: "Event", foreign_key: :author_id, order: "id DESC"
-
-  has_many :projects,       through: :users_projects
-
-  has_many :user_team_user_relationships, dependent: :destroy
-
-  has_many :user_teams,                      through: :user_team_user_relationships
-  has_many :user_team_project_relationships, through: :user_teams
-  has_many :team_projects,                   through: :user_team_project_relationships
+  has_many :recent_events,
+    class_name: "Event",
+    foreign_key: :author_id,
+    order: "id DESC"
 
+  #
+  # Validations
+  #
   validates :name, presence: true
   validates :email, presence: true, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/ }
   validates :bio, length: { within: 0..255 }
@@ -87,10 +110,27 @@ class User < ActiveRecord::Base
 
   delegate :path, to: :namespace, allow_nil: true, prefix: true
 
+  state_machine :state, initial: :active do
+    after_transition any => :blocked do |user, transition|
+      # Remove user from all projects and
+      user.users_projects.find_each do |membership|
+        return false unless membership.destroy
+      end
+    end
+
+    event :block do
+      transition active: :blocked
+    end
+
+    event :activate do
+      transition blocked: :active
+    end
+  end
+
   # Scopes
   scope :admins, -> { where(admin:  true) }
-  scope :blocked, -> { where(blocked:  true) }
-  scope :active, -> { where(blocked:  false) }
+  scope :blocked, -> { with_state(:blocked) }
+  scope :active, -> { with_state(:active) }
   scope :alphabetically, -> { order('name ASC') }
   scope :in_team, ->(team){ where(id: team.member_ids) }
   scope :not_in_team, ->(team){ where('users.id NOT IN (:ids)', ids: team.member_ids) }
@@ -260,17 +300,6 @@ class User < ActiveRecord::Base
     MergeRequest.cared(self)
   end
 
-  # Remove user from all projects and
-  # set blocked attribute to true
-  def block
-    users_projects.find_each do |membership|
-      return false unless membership.destroy
-    end
-
-    self.blocked = true
-    save
-  end
-
   def projects_limit_percent
     return 100 if projects_limit.zero?
     (personal_projects.count.to_f / projects_limit) * 100
index 2f3091c..0cb84ed 100644 (file)
@@ -11,7 +11,7 @@
 #
 
 class UserTeam < ActiveRecord::Base
-  attr_accessible :name, :owner_id, :path
+  attr_accessible :name, :description, :owner_id, :path
 
   belongs_to :owner, class_name: User
 
@@ -26,6 +26,7 @@ class UserTeam < ActiveRecord::Base
             length: { within: 0..255 },
             format: { with: Gitlab::Regex.name_regex,
                       message: "only letters, digits, spaces & '_' '-' '.' allowed." }
+  validates :description, length: { within: 0..255 }
   validates :path, uniqueness: true, presence: true, length: { within: 1..255 },
             format: { with: Gitlab::Regex.path_regex,
                       message: "only letters, digits & '_' '-' '.' allowed. Letter should be first" }
index 592e295..29e2404 100644 (file)
@@ -27,7 +27,7 @@ class IssueObserver < ActiveRecord::Observer
 
   def create_note(issue)
     Note.create_status_change_note(issue, current_user, issue.state)
-    [issue.author, issue.assignee].compact.each do |recipient|
+    [issue.author, issue.assignee].compact.uniq.each do |recipient|
       Notify.delay.issue_status_changed_email(recipient.id, issue.id, issue.state, current_user.id)
     end
   end
index c1179ed..6c461e0 100644 (file)
@@ -2,7 +2,8 @@ class UserObserver < ActiveRecord::Observer
   def after_create(user)
     log_info("User \"#{user.name}\" (#{user.email}) was created")
 
-    Notify.delay.new_user_email(user.id, user.password)
+    # Dont email omniauth created users
+    Notify.delay.new_user_email(user.id, user.password) unless user.extern_uid?
   end
 
   def after_destroy user
index 40d57c6..208ccf6 100644 (file)
@@ -19,6 +19,8 @@ class GitPushService
     # Collect data for this git push
     @push_data = post_receive_data(oldrev, newrev, ref)
 
+    create_push_event
+
     project.ensure_satellite_exists
     project.discover_default_branch
 
@@ -27,8 +29,6 @@ class GitPushService
       project.execute_hooks(@push_data.dup)
       project.execute_services(@push_data.dup)
     end
-
-    create_push_event
   end
 
   # This method provide a sample data
index f91a3cd..35d9517 100644 (file)
@@ -25,7 +25,7 @@ class ProjectTransferService
 
       Gitlab::ProjectMover.new(project, old_dir, new_dir).execute
 
-      save!
+      project.save!
     end
   rescue Gitlab::ProjectMover::ProjectMoveError => ex
     raise Project::TransferError.new(ex.message)
index dce0449..bb1398f 100644 (file)
@@ -1,4 +1,4 @@
-%h3.page_title Rename Group
+%h3.page_title Edit Group
 %hr
 = form_for [:admin, @group] do |f|
   - if @group.errors.any?
     .input
       = f.text_field :name, placeholder: "Example Group", class: "xxlarge"
 
-
+  .clearfix.group-description-holder
+    = f.label :description, "Details"
+    .input
+      = f.text_area :description, maxlength: 250, class: "xxlarge js-gfm-input", rows: 4
 
   .clearfix.group_name_holder
     = f.label :path do
@@ -24,5 +27,5 @@
         %li It will change the git path to repositories under this group.
 
   .form-actions
-    = f.submit 'Rename group', class: "btn btn-remove"
+    = f.submit 'Edit group', class: "btn btn-remove"
     = link_to  'Cancel', admin_groups_path, class: "btn btn-cancel"
index 6d5a293..b10a739 100644 (file)
@@ -17,6 +17,7 @@
         Name
         %i.icon-sort-down
       %th Path
+      %th Description
       %th Projects
       %th Owner
       %th.cred Danger Zone!
     %tr
       %td
         %strong= link_to group.name, [:admin, group]
+      %td= truncate group.description
       %td= group.path
       %td= group.projects.count
       %td
         = link_to group.owner_name, admin_user_path(group.owner)
       %td.bgred
-        = link_to 'Rename', edit_admin_group_path(group), id: "edit_#{dom_id(group)}", class: "btn btn-small"
+        = link_to 'Edit', edit_admin_group_path(group), id: "edit_#{dom_id(group)}", class: "btn btn-small"
         = link_to 'Destroy', [:admin, group], confirm: "REMOVE #{group.name}? Are you sure?", method: :delete, class: "btn btn-small btn-remove"
 = paginate @groups, theme: "admin"
index 60c6fa5..3fa63e1 100644 (file)
@@ -9,8 +9,14 @@
       Group name is
     .input
       = f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left"
-      &nbsp;
-      = f.submit 'Create group', class: "btn btn-primary"
+  .clearfix.group-description-holder
+    = f.label :description, "Details"
+    .input
+      = f.text_area :description, maxlength: 250, class: "xxlarge js-gfm-input", rows: 4
+
+  .form-actions
+    = f.submit 'Create group', class: "btn btn-primary"
+
   %hr
   .padded
     %ul
index 90f8fc0..63ea78f 100644 (file)
       &nbsp;
       = link_to edit_admin_group_path(@group), class: "btn btn-small pull-right" do
         %i.icon-edit
-        Rename
+        Edit
+  %tr
+    %td
+      %b
+        Description:
+    %td
+      = @group.description
   %tr
     %td
       %b
index ebf6992..29b90bd 100644 (file)
       = f.label :issues_enabled, "Issues"
       .input= f.check_box :issues_enabled
 
+    - if Project.issues_tracker.values.count > 1
+      .clearfix
+        = f.label :issues_tracker, "Issues tracker", class: 'control-label'
+        .input= f.select(:issues_tracker, Project.issues_tracker.values, {}, { disabled: !@project.issues_enabled })
+
+      .clearfix
+        = f.label :issues_tracker_id, "Project name or id in issues tracker", class: 'control-label'
+        .input= f.text_field :issues_tracker_id, class: "xxlarge", disabled: !@project.can_have_issues_tracker_id?
+
     .clearfix
       = f.label :merge_requests_enabled, "Merge Requests"
       .input= f.check_box :merge_requests_enabled
index 9282398..0a3d993 100644 (file)
@@ -1,4 +1,4 @@
-%h3.page_title Rename Team
+%h3.page_title Edit Team
 %hr
 = form_for @team, url: admin_team_path(@team), method: :put do |f|
   - if @team.errors.any?
     .input
       = f.text_field :name, placeholder: "Example Team", class: "xxlarge"
 
+  .clearfix.team-description-holder
+    = f.label :description, "Details"
+    .input
+      = f.text_area :description, maxlength: 250, class: "xxlarge js-gfm-input", rows: 4
+
   .clearfix.team_name_holder
     = f.label :path do
       %span.cred Team path is
@@ -19,5 +24,5 @@
         %li It will change web url for access team and team projects.
 
   .form-actions
-    = f.submit 'Rename team', class: "btn btn-remove"
+    = f.submit 'Edit team', class: "btn btn-remove"
     = link_to  'Cancel', admin_teams_path, class: "btn btn-cancel"
index bb0487d..3690d6d 100644 (file)
@@ -16,6 +16,7 @@
       %th
         Name
         %i.icon-sort-down
+      %th Description
       %th Path
       %th Projects
       %th Members
     %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
-        = link_to team.owner.name, admin_user_path(team.owner)
+        - if team.owner
+          = link_to team.owner.name, admin_user_path(team.owner)
+        - else
+          (deleted)
       %td.bgred
-        = link_to 'Rename', edit_admin_team_path(team), id: "edit_#{dom_id(team)}", class: "btn btn-small"
+        = 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"
 
 = paginate @teams, theme: "admin"
index 5d55a79..1c90cb2 100644 (file)
@@ -9,8 +9,15 @@
       Team name is
     .input
       = f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left"
-      &nbsp;
-      = f.submit 'Create team', class: "btn btn-primary"
+
+  .clearfix.team-description-holder
+    = f.label :description, "Details"
+    .input
+      = f.text_area :description, maxlength: 250, class: "xxlarge js-gfm-input", rows: 4
+
+  .form-actions
+    = f.submit 'Create team', class: "btn btn-primary"
+
   %hr
   .padded
     %ul
index e5d0799..abdfada 100644 (file)
       &nbsp;
       = link_to edit_admin_team_path(@team), class: "btn btn-small pull-right" do
         %i.icon-edit
-        Rename
+        Edit
+  %tr
+    %td
+      %b
+        Description:
+    %td
+      = @team.description
   %tr
     %td
       %b
index 4887633..1d1fe34 100644 (file)
@@ -61,7 +61,7 @@
         .span4
           - unless @admin_user.new_record?
             .alert.alert-error
-              - if @admin_user.blocked
+              - if @admin_user.blocked?
                 %p This user is blocked and is not able to login to GitLab
                 = link_to 'Unblock User', unblock_admin_user_path(@admin_user), method: :put, class: "btn btn-small"
               - else
index f5bb8b0..9da2871 100644 (file)
@@ -53,7 +53,7 @@
               &nbsp;
               = link_to 'Edit', edit_admin_user_path(user), id: "edit_#{dom_id(user)}", class: "btn btn-small"
               - unless user == current_user
-                - if user.blocked
+                - if user.blocked?
                   = link_to 'Unblock', unblock_admin_user_path(user), method: :put, class: "btn btn-small success"
                 - else
                   = link_to 'Block', block_admin_user_path(user), confirm: 'USER WILL BE BLOCKED! Are you sure?', method: :put, class: "btn btn-small btn-remove"
index c5d6019..2129cee 100644 (file)
@@ -3,7 +3,7 @@
     %h3.page_title
       = image_tag gravatar_icon(@admin_user.email, 90), class: "avatar s90"
       = @admin_user.name
-      - if @admin_user.blocked
+      - if @admin_user.blocked?
         %span.cred (Blocked)
       - if @admin_user.admin
         %span.cred (Admin)
index 1913209..869d1f9 100644 (file)
@@ -1,4 +1,4 @@
-- @commits.group_by { |c| c.committed_date.to_date }.each do |day, commits|
+- @commits.group_by { |c| c.committed_date.to_date }.sort.reverse.each do |day, commits|
   %div.ui-box
     %h5.title
       %i.icon-calendar
index 19665ce..199785e 100644 (file)
     = event.project_name
 
 .event-body
-  %i.icon-comment-alt.event-note-icon
-  %span.event-note
-    = markdown truncate(event.target.note, length: 70)
+  .event-note
+    .md
+      %i.icon-comment-alt.event-note-icon
+      = sanitize(markdown(truncate(event.target.note, length: 150)), tags: %w(a img b pre p))
     - note = event.target
     - if note.attachment.url
       = link_to note.attachment.secure_url, target: "_blank", class: 'note-file-attach' do
index 41ebf60..bf16b70 100644 (file)
@@ -9,8 +9,15 @@
       Group name is
     .input
       = f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left"
-      &nbsp;
-      = f.submit 'Save group', class: "btn btn-save"
+
+  .clearfix.group-description-holder
+    = f.label :description, "Details"
+    .input
+      = f.text_area :description, maxlength: 250, class: "xxlarge js-gfm-input", rows: 4
+
+  .form-actions
+    = f.submit 'Save group', class: "btn btn-save"
+
 %hr
 
 
index 73be474..36ee492 100644 (file)
@@ -9,9 +9,16 @@
       Group name is
     .input
       = f.text_field :name, placeholder: "Ex. OpenSource", class: "xxlarge left"
-      &nbsp;
-      = f.submit 'Create group', class: "btn btn-create"
-  %hr
+
+  .clearfix.group-description-holder
+    = f.label :description, "Details"
+    .input
+      = f.text_area :description, maxlength: 250, class: "xxlarge js-gfm-input", rows: 4
+
+  .form-actions
+    = f.submit 'Create group', class: "btn btn-create"
+
+
   .padded
     %ul
       %li Group is kind of directory for several projects
index a140b40..81694b8 100644 (file)
@@ -12,6 +12,9 @@
       %p.nothing_here_message Project activity will be displayed here
     .loading.hide
   .side.span4
+    - if @group.description.present?
+      .description.well.light
+        = @group.description
     = render "projects", projects: @projects
     %div
       %span.rss-icon
index 21efaa5..b621f11 100644 (file)
@@ -6,7 +6,10 @@
           Open
       %li{class: ("active" if params[:status] == 'assigned-to-me')}
         = link_to project_issues_path(@project, status: 'assigned-to-me') do
-          Assigned To Me
+          Assigned to me
+      %li{class: ("active" if params[:status] == 'created-by-me')}
+        = link_to project_issues_path(@project, status: 'created-by-me') do
+          Created by me
       %li{class: ("active" if params[:status] == 'closed')}
         = link_to project_issues_path(@project, status: 'closed') do
           Closed
index 9961ce8..a3bed59 100644 (file)
@@ -1,3 +1,8 @@
-- if text = alert || notice
-  #flash-container
-    %h4= text
+.flash-container
+  - if alert
+    .alert
+      %span= alert
+
+  - elsif notice
+    .alert.alert-info
+      %span= notice
index 4b4f5da..eb83fd2 100644 (file)
@@ -7,6 +7,7 @@
   = stylesheet_link_tag    "application"
   = javascript_include_tag "application"
   = csrf_meta_tags
+  = include_gon
 
   -# Atom feed
   - if current_user
index 8f4f3d7..1f3ce2f 100644 (file)
@@ -8,6 +8,9 @@
         %span.separator
       %h1.project_name= title
       %ul.nav
+        %li
+          = link_to public_root_path, title: "Public area", class: 'has_bottom_tooltip', 'data-original-title' => 'Public area' do
+            %i.icon-globe
         - if current_user.is_admin?
           %li
             = link_to admin_root_path, title: "Admin area", class: 'has_bottom_tooltip', 'data-original-title' => 'Admin area' do
index a01886c..00a08e6 100644 (file)
@@ -2,8 +2,8 @@
 %html{ lang: "en"}
   = render "layouts/head", title: "Admin area"
   %body{class: "#{app_theme} admin"}
-    = render "layouts/flash"
     = render "layouts/head_panel", title: "Admin area"
+    = render "layouts/flash"
     .container
       %ul.main_menu
         = nav_link(controller: :dashboard, html_options: {class: 'home'}) do
index 7ee4423..90c2653 100644 (file)
@@ -2,8 +2,8 @@
 %html{ lang: "en"}
   = render "layouts/head", title: "Dashboard"
   %body{class: "#{app_theme} application"}
-    = render "layouts/flash"
     = render "layouts/head_panel", title: "Dashboard"
+    = render "layouts/flash"
     .container
       %ul.main_menu
         = nav_link(path: 'dashboard#show', html_options: {class: 'home'}) do
index 36c6b4c..a9758f1 100644 (file)
@@ -3,4 +3,6 @@
   = render "layouts/head"
   %body.ui_basic.login-page
     = render "layouts/flash"
-    .container= yield
+    .container
+      .content
+        = yield
index 3554d88..b939587 100644 (file)
@@ -2,8 +2,8 @@
 %html{ lang: "en"}
   = render "layouts/head", title: "Error"
   %body{class: "#{app_theme} application"}
-    = render "layouts/flash"
     = render "layouts/head_panel", title: ""
+    = render "layouts/flash"
     .container
       .content
         %center.padded.prepend-top-20
index 9057ad5..2c144de 100644 (file)
@@ -2,8 +2,8 @@
 %html{ lang: "en"}
   = render "layouts/head", title: "#{@group.name}"
   %body{class: "#{app_theme} application"}
-    = render "layouts/flash"
     = render "layouts/head_panel", title: "group: #{@group.name}"
+    = render "layouts/flash"
     .container
       %ul.main_menu
         = nav_link(path: 'groups#show', html_options: {class: 'home'}) do
index 57f250c..611063e 100644 (file)
@@ -2,8 +2,8 @@
 %html{ lang: "en"}
   = render "layouts/head", title: "Profile"
   %body{class: "#{app_theme} profile"}
-    = render "layouts/flash"
     = render "layouts/head_panel", title: "Profile"
+    = render "layouts/flash"
     .container
       %ul.main_menu
         = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
index 13fb863..37d0f16 100644 (file)
@@ -2,8 +2,8 @@
 %html{ lang: "en"}
   = render "layouts/head", title: @project.name_with_namespace
   %body{class: "#{app_theme} project"}
-    = render "layouts/flash"
     = render "layouts/head_panel", title: project_title(@project)
+    = render "layouts/flash"
     - if can?(current_user, :download_code, @project)
       = render 'shared/no_ssh'
 
             = nav_link(controller: %w(graph)) do
               = link_to "Network", project_graph_path(@project, @ref || @repository.root_ref)
 
-        - if @project.issues_enabled
+        - if @project.issues_enabled 
           = nav_link(controller: %w(issues milestones labels)) do
-            = link_to  project_issues_filter_path(@project)  do
+            = link_to url_for_project_issues do
               Issues
-              %span.count.issue_counter= @project.issues.opened.count
+              - if @project.used_default_issues_tracker?
+                %span.count.issue_counter= @project.issues.opened.count
 
         - if @project.repo_exists? && @project.merge_requests_enabled
           = nav_link(controller: :merge_requests) do
index 19bbc37..e5e08aa 100644 (file)
@@ -2,8 +2,8 @@
 %html{ lang: "en"}
   = render "layouts/head", title: "#{@team.name}"
   %body{class: "#{app_theme} application"}
-    = render "layouts/flash"
     = render "layouts/head_panel", title: "team: #{@team.name}"
+    = render "layouts/flash"
     .container
       %ul.main_menu
         = nav_link(path: 'teams#show', html_options: {class: 'home'}) do
index 64f25a5..d4271c5 100644 (file)
@@ -1,9 +1,9 @@
-- unless can?(current_user, :accept_mr, @project)
+- unless @allowed_to_merge
   .alert
-    %strong Only masters can accept MR
+    %strong You don't have enough permissions to merge this MR
 
 
-- if @merge_request.opened? && @commits.any? && can?(current_user, :accept_mr, @project)
+- if @show_merge_controls
   .automerge_widget.can_be_merged{style: "display:none"}
     .alert.alert-success
       %span
diff --git a/app/views/notify/issue_status_changed_email.text.erb b/app/views/notify/issue_status_changed_email.text.erb
new file mode 100644 (file)
index 0000000..bbca347
--- /dev/null
@@ -0,0 +1,4 @@
+Issue was <%= @issue_status %> by <%= @updated_by.name %>
+
+Issue <%= @issue.id %>: <%= url_for(project_issue_url(@issue.project, @issue)) %>
+
diff --git a/app/views/notify/new_issue_email.text.erb b/app/views/notify/new_issue_email.text.erb
new file mode 100644 (file)
index 0000000..5ed55c3
--- /dev/null
@@ -0,0 +1,4 @@
+New Issue was created and assigned to you.
+          
+
+Issue <%= @issue.id %>: <%= url_for(project_issue_url(@issue.project, @issue)) %>
diff --git a/app/views/notify/new_merge_request_email.text.erb b/app/views/notify/new_merge_request_email.text.erb
new file mode 100644 (file)
index 0000000..3393d83
--- /dev/null
@@ -0,0 +1,9 @@
+New Merge Request <%= @merge_request.id %>
+
+<%= url_for(project_merge_request_url(@merge_request.project, @merge_request)) %>
+          
+
+Branches: <%= @merge_request.source_branch %> to <%= @merge_request.target_branch %>
+Author:   <%= @merge_request.author_name %>
+Asignee:  <%= @merge_request.assignee_name %>
+
diff --git a/app/views/notify/new_user_email.text.erb b/app/views/notify/new_user_email.text.erb
new file mode 100644 (file)
index 0000000..94072d7
--- /dev/null
@@ -0,0 +1,10 @@
+Hi <%= @user.name %>!
+          
+Administrator created account for you. Now you are a member of company GitLab application.
+          
+login.................. <%= @user.email %>
+<% unless Gitlab.config.gitlab.signup_enabled %>
+  password............... <%= @password %>
+<% end %>
+
+Click here to login: <%= url_for(root_url) %>
diff --git a/app/views/notify/note_commit_email.text.erb b/app/views/notify/note_commit_email.text.erb
new file mode 100644 (file)
index 0000000..aab8e5c
--- /dev/null
@@ -0,0 +1,9 @@
+New comment for Commit <%= @commit.short_id %>
+
+<%= url_for(project_commit_url(@note.project, id: @commit.id, anchor: "note_#{@note.id}")) %>
+
+
+Author: <%= @note.author_name %>
+
+<%= @note.note %>
+
diff --git a/app/views/notify/note_issue_email.text.erb b/app/views/notify/note_issue_email.text.erb
new file mode 100644 (file)
index 0000000..a476b28
--- /dev/null
@@ -0,0 +1,9 @@
+New comment for Issue <%= @issue.id %>
+
+<%= url_for(project_issue_url(@issue.project, @issue, anchor: "note_#{@note.id}")) %>
+
+
+Author: <%= @note.author_name %>
+
+<%= @note.note %>
+
diff --git a/app/views/notify/note_merge_request_email.text.erb b/app/views/notify/note_merge_request_email.text.erb
new file mode 100644 (file)
index 0000000..26c73bd
--- /dev/null
@@ -0,0 +1,9 @@
+New comment for Merge Request <%= @merge_request.id %>
+
+<%= url_for(project_merge_request_url(@merge_request.project, @merge_request, anchor: "note_#{@note.id}")) %>
+      
+
+<%= @note.author_name %>
+                  
+<%= @note.note %>
+
diff --git a/app/views/notify/note_wall_email.text.erb b/app/views/notify/note_wall_email.text.erb
new file mode 100644 (file)
index 0000000..ea1b7ef
--- /dev/null
@@ -0,0 +1,9 @@
+New message on the project wall <%= @note.project %>
+
+<%= url_for(wall_project_url(@note.project, anchor: "note_#{@note.id}")) %>
+          
+
+<%= @note.author_name %>
+
+<%= @note.note %>
+
diff --git a/app/views/notify/project_access_granted_email.text.erb b/app/views/notify/project_access_granted_email.text.erb
new file mode 100644 (file)
index 0000000..077c3b8
--- /dev/null
@@ -0,0 +1,4 @@
+
+You have been granted <%= @users_project.project_access_human %> access to project <%= @project.name_with_namespace %>
+
+<%= url_for(project_url(@project)) %>
diff --git a/app/views/notify/project_was_moved_email.text.erb b/app/views/notify/project_was_moved_email.text.erb
new file mode 100644 (file)
index 0000000..da123c2
--- /dev/null
@@ -0,0 +1,8 @@
+Project was moved to another location
+          
+The project is now located under 
+<%= url_for(link_to project_url(@project)) %>
+          
+
+To update the remote url in your local repository run:
+  git remote set-url origin <%= @project.ssh_url_to_repo %>
diff --git a/app/views/notify/reassigned_issue_email.text.erb b/app/views/notify/reassigned_issue_email.text.erb
new file mode 100644 (file)
index 0000000..4970441
--- /dev/null
@@ -0,0 +1,7 @@
+Reassigned Issue <%= @issue.id %>
+
+<%= url_for(project_issue_url(@issue.project, @issue)) %>
+          
+
+Assignee changed from <%= @previous_assignee.name %> to <%= @issue.assignee_name %>
+
diff --git a/app/views/notify/reassigned_merge_request_email.text.erb b/app/views/notify/reassigned_merge_request_email.text.erb
new file mode 100644 (file)
index 0000000..1af4ab5
--- /dev/null
@@ -0,0 +1,7 @@
+Reassigned Merge Request <%= @merge_request.id %>
+
+<%= url_for(project_merge_request_url(@merge_request.project, @merge_request)) %>
+          
+
+Assignee changed from <%= @previous_assignee.name %> to <%= @merge_request.assignee_name %>
+
index 0336654..b78c70b 100644 (file)
         = f.check_box :issues_enabled
         %span.descr Lightweight issue tracking system for this project
 
+    - if Project.issues_tracker.values.count > 1
+      .control-group
+        = f.label :issues_tracker, "Issues tracker", class: 'control-label'
+        .input= f.select(:issues_tracker, Project.issues_tracker.values, {}, { disabled: !@project.issues_enabled })
+
+      .clearfix
+        = f.label :issues_tracker_id, "Project name or id in issues tracker", class: 'control-label'
+        .input= f.text_field :issues_tracker_id, class: "xxlarge", disabled: !@project.can_have_issues_tracker_id?
+
     .control-group
       = f.label :merge_requests_enabled, "Merge Requests", class: 'control-label'
       .controls
index 22aaaf0..07132e6 100644 (file)
@@ -5,14 +5,14 @@
   %fieldset
     %legend Git global setup:
     %pre.dark
-      = preserve do
+      :preserve
         git config --global user.name "#{current_user.name}"
         git config --global user.email "#{current_user.email}"
 
   %fieldset
     %legend Create Repository
     %pre.dark
-      = preserve do
+      :preserve
         mkdir #{@project.path}
         cd #{@project.path}
         git init
@@ -25,7 +25,7 @@
   %fieldset
     %legend Existing Git Repo?
     %pre.dark
-      = preserve do
+      :preserve
         cd existing_git_repo
         git remote add origin #{@project.url_to_repo}
         git push -u origin master
index 21e9d2e..52e01c3 100644 (file)
@@ -12,5 +12,7 @@
         .pull-right
           %pre.dark.tiny git clone #{project.http_url_to_repo}
 
+  - unless @projects.present?
+    %h3.nothing_here_message No public projects
 
 = paginate @projects, theme: "admin"
index 7b5de4a..bd9ca72 100644 (file)
@@ -1,5 +1,4 @@
 .input-prepend.project_clone_holder
   %button{class: "btn active", :"data-clone" => @project.ssh_url_to_repo} SSH
   %button{class: "btn", :"data-clone" => @project.http_url_to_repo}= Gitlab.config.gitlab.protocol.upcase
-
-  = text_field_tag :project_clone, @project.url_to_repo, class: "one_click_select input-xxlarge"
+  = text_field_tag :project_clone, @project.url_to_repo, class: "one_click_select input-xxlarge", readonly: true
index e7cba0b..e0485f4 100644 (file)
@@ -20,7 +20,7 @@
           %span.label This is you!
         - if @project.namespace_owner == user
           %span.label Owner
-        - elsif user.blocked
+        - elsif user.blocked?
           %span.label Blocked
         - elsif allow_admin
           = link_to project_team_member_path(@project, user), confirm: remove_from_project_team_message(@project, user), method: :delete, class: "btn-tiny btn btn-remove" do
index 751fe94..95c91b5 100644 (file)
         .input
           = f.text_field :name, placeholder: "Ex. OpenSource", class: "xlarge left"
 
+      .clearfix.team-description-holder
+        = f.label :description, "Details"
+        .input
+          = f.text_area :description, maxlength: 250, class: "xlarge js-gfm-input", rows: 4
+
       .clearfix
         = f.label :path do
           Team path is
         .input
           = f.text_field :path, placeholder: "opensource", class: "xlarge left"
+
       .form-actions
-        = f.submit 'Save team changes', class: "btn btn-save"
+        = f.submit 'Save team changes', class: "btn btn-primary"
   .span5
     .ui-box
       %h5.title Remove team
@@ -26,4 +32,3 @@
         %p
           Removed team can not be restored!
         = link_to 'Remove team', team_path(@team), method: :delete, confirm: "You are sure?", class: "btn btn-remove btn-small"
-
index 3aa2db8..5975810 100644 (file)
@@ -23,7 +23,7 @@
         %span.btn.disabled This is you!
       - if @team.owner == user
         %span.btn.disabled Owner
-      - elsif user.blocked
+      - elsif user.blocked?
         %span.btn.disabled.blocked Blocked
       - elsif allow_admin
         = link_to team_member_path(@team, user), confirm: remove_from_user_team_message(@team, user), method: :delete, class: "btn-tiny btn btn-remove", title: "Remove from team" do
index 7089f79..99d3082 100644 (file)
@@ -9,9 +9,15 @@
       Team name is
     .input
       = f.text_field :name, placeholder: "Ex. Ruby Developers", class: "xxlarge left"
-      &nbsp;
-      = f.submit 'Create team', class: "btn btn-create"
-  %hr
+
+  .clearfix.team-description-holder
+    = f.label :description, "Details"
+    .input
+      = f.text_area :description, maxlength: 250, class: "xxlarge js-gfm-input", rows: 4
+
+  .form-actions
+    = f.submit 'Create team', class: "btn btn-create"
+
   .padded
     %ul
       %li All created teams are public (users can view who enter into team and which project are assigned for this team)
index d6e80e2..43cc026 100644 (file)
@@ -11,6 +11,9 @@
       %p.nothing_here_message Projects activity will be displayed here
     .loading.hide
   .side.span4
+    - if @team.description.present?
+      .description.well.light
+        = @team.description
     = render "projects", projects: @projects
     %div
       %span.rss-icon
index 62761c8..3fb1738 100644 (file)
@@ -1,5 +1,5 @@
 # # # # # # # # # # # # # # # # # # 
-# Gitlab application config file  #
+# GitLab application config file  #
 # # # # # # # # # # # # # # # # # #
 #
 # How to use:
@@ -37,9 +37,25 @@ production: &base
     # signup_enabled: true          # default: false - Account passwords are not sent via the email if signup is enabled.
     # username_changing_enabled: false # default: true - User can change her username/namespace
 
+
+  ## External issues trackers
+  issues_tracker:
+    redmine:
+      ## If not nil, link 'Issues' on project page will be replaced with this
+      ## Use placeholders:
+      ##  :project_id        - GitLab project identifier
+      ##  :issues_tracker_id - Project Name or Id in external issue tracker
+      project_url: "http://redmine.sample/projects/:issues_tracker_id"
+      ## If not nil, links from /#\d/ entities from commit messages will replaced with this
+      ## Use placeholders:
+      ##  :project_id        - GitLab project identifier
+      ##  :issues_tracker_id - Project Name or Id in external issue tracker
+      ##  :id                - Issue id (from commit messages)
+      issues_url: "http://redmine.sample/issues/:id"
+
   ## Gravatar
   gravatar:
-    enabled: true                 # Use user avatar images from Gravatar.com (default: true)
+    enabled: true                 # Use user avatar image from Gravatar.com (default: true)
     # plain_url: "http://..."     # default: http://www.gravatar.com/avatar/%{hash}?s=%{size}&d=mm
     # ssl_url:   "https://..."    # default: https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=mm
 
@@ -60,22 +76,21 @@ production: &base
     bind_dn: '_the_full_dn_of_the_user_you_will_bind_with'
     password: '_the_password_of_the_bind_user'
 
-  ## Omniauth settings
+  ## OmniAuth settings
   omniauth:
-    # Enable ability for users
-    # Allow logging in via Twitter, Google, etc. using Omniauth providers
+    # Allow login via Twitter, Google, etc. using OmniAuth providers
     enabled: false
 
     # CAUTION!
-    # This allows users to login without having a user account first (default: false)
+    # This allows users to login without having a user account first (default: false).
     # User accounts will be created automatically when authentication was successful.
     allow_single_sign_on: false
-    # Locks down those users until they have been cleared by the admin (default: true)
+    # Locks down those users until they have been cleared by the admin (default: true).
     block_auto_created_users: true
 
     ## Auth providers
-    # Uncomment the lines and fill in the data of the auth provider you want to use
-    # If your favorite auth provider is not listed you can user others:
+    # Uncomment the following lines and fill in the data of the auth provider you want to use
+    # If your favorite auth provider is not listed you can use others:
     # see https://github.com/gitlabhq/gitlabhq/wiki/Using-Custom-Omniauth-Providers
     # The 'app_id' and 'app_secret' parameters are always passed as the first two
     # arguments, followed by optional 'args' which can be either a hash or an array.
@@ -114,7 +129,7 @@ production: &base
     upload_pack: true
     receive_pack: true
 
-    # If you use non-standart ssh port you need to specify it
+    # If you use non-standard ssh port you need to specify it
     # ssh_port: 22
 
   ## Git settings
@@ -122,10 +137,10 @@ production: &base
   # Use the default values unless you really know what you are doing
   git:
     bin_path: /usr/bin/git
-    # Max size of git object like commit, in bytes
-    # This value can be increased if you have very large commits
+    # Max size of a git object (e.g. a commit), in bytes
+    # This value can be increased if you have very large commits
     max_size: 5242880 # 5.megabytes
-    # Git timeout to read commit, in seconds
+    # Git timeout to read commit, in seconds
     timeout: 10
 
 development:
@@ -133,6 +148,10 @@ development:
 
 test:
   <<: *base
+  issues_tracker:
+    redmine:
+      project_url: "http://redmine/projects/:issues_tracker_id"
+      issues_url: "http://redmine/:project_id/:issues_tracker_id/:id"
 
 staging:
   <<: *base
index f7d18e6..ac35eef 100644 (file)
@@ -42,6 +42,8 @@ Settings['omniauth'] ||= Settingslogic.new({})
 Settings.omniauth['enabled']      = false if Settings.omniauth['enabled'].nil?
 Settings.omniauth['providers']  ||= []
 
+Settings['issues_tracker']  ||= {}
+
 #
 # GitLab
 #
@@ -50,7 +52,7 @@ Settings.gitlab['default_projects_limit'] ||=  10
 Settings.gitlab['host']       ||= 'localhost'
 Settings.gitlab['https']        = false if Settings.gitlab['https'].nil?
 Settings.gitlab['port']       ||= Settings.gitlab.https ? 443 : 80
-Settings.gitlab['relative_url_root'] ||= ''
+Settings.gitlab['relative_url_root'] ||= ENV['RAILS_RELATIVE_URL_ROOT'] || ''
 Settings.gitlab['protocol']   ||= Settings.gitlab.https ? "https" : "http"
 Settings.gitlab['email_from'] ||= "gitlab@#{Settings.gitlab.host}"
 Settings.gitlab['support_email']  ||= Settings.gitlab.email_from
index 97946c5..9c39763 100644 (file)
@@ -99,7 +99,7 @@ Devise.setup do |config|
 
   # ==> Configuration for :validatable
   # Range for password length. Default is 6..128.
-  config.password_length = 6..128
+  config.password_length = 6..128
 
   # Email regex used to validate email formats. It simply asserts that
   # an one (and only one) @ exists in the given string. This is mainly
index 3b763cf..275273a 100644 (file)
@@ -17,6 +17,7 @@ en:
       unauthenticated: 'You need to sign in before continuing.'
       unconfirmed: 'You have to confirm your account before continuing.'
       locked: 'Your account is locked.'
+      not_found_in_database: 'Invalid email or password.'
       invalid: 'Invalid email or password.'
       invalid_token: 'Invalid authentication token.'
       timeout: 'Your session expired, please sign in again to continue.'
index 10536a6..c8e7c8e 100644 (file)
@@ -49,7 +49,7 @@ Gitlab::Application.routes.draw do
   #
   # Attachments serving
   #
-  get 'files/:type/:id/:filename' => 'files#download', constraints: { id: /\d+/, type: /[a-z]+/, filename: /[a-zA-Z.0-9_\-\+]+/ }
+  get 'files/:type/:id/:filename' => 'files#download', constraints: { id: /\d+/, type: /[a-z]+/, filename:  /.+/ }
 
   #
   # Admin Area
diff --git a/db/migrate/20130123114545_add_issues_tracker_to_project.rb b/db/migrate/20130123114545_add_issues_tracker_to_project.rb
new file mode 100644 (file)
index 0000000..288d0f0
--- /dev/null
@@ -0,0 +1,5 @@
+class AddIssuesTrackerToProject < ActiveRecord::Migration
+  def change
+    add_column :projects, :issues_tracker, :string, default: :gitlab, null: false
+  end
+end
diff --git a/db/migrate/20130206084024_add_description_to_namsespace.rb b/db/migrate/20130206084024_add_description_to_namsespace.rb
new file mode 100644 (file)
index 0000000..ef02e48
--- /dev/null
@@ -0,0 +1,5 @@
+class AddDescriptionToNamsespace < ActiveRecord::Migration
+  def change
+    add_column :namespaces, :description, :string, default: '', null: false
+  end
+end
diff --git a/db/migrate/20130207104426_add_description_to_teams.rb b/db/migrate/20130207104426_add_description_to_teams.rb
new file mode 100644 (file)
index 0000000..6d03777
--- /dev/null
@@ -0,0 +1,5 @@
+class AddDescriptionToTeams < ActiveRecord::Migration
+  def change
+    add_column :user_teams, :description, :string, default: '', null: false
+  end
+end
diff --git a/db/migrate/20130211085435_add_issues_tracker_id_to_project.rb b/db/migrate/20130211085435_add_issues_tracker_id_to_project.rb
new file mode 100644 (file)
index 0000000..71763d1
--- /dev/null
@@ -0,0 +1,5 @@
+class AddIssuesTrackerIdToProject < ActiveRecord::Migration
+  def change
+    add_column :projects, :issues_tracker_id, :string
+  end
+end
index 0614a5c..9fa9620 100644 (file)
@@ -1,14 +1,14 @@
 class ConvertClosedToStateInIssue < ActiveRecord::Migration
   def up
     Issue.transaction do
-      Issue.where(closed: true).update_all("state = 'closed'")
-      Issue.where(closed: false).update_all("state = 'opened'")
+      Issue.where(closed: true).update_all(state: :closed)
+      Issue.where(closed: false).update_all(state: :opened)
     end
   end
 
   def down
     Issue.transaction do
-      Issue.where(state: :closed).update_all("closed = 1")
+      Issue.where(state: :closed).update_all(closed: true)
     end
   end
 end
index 5e7477d..ebb7ae5 100644 (file)
@@ -1,9 +1,9 @@
 class ConvertClosedToStateInMergeRequest < ActiveRecord::Migration
   def up
     MergeRequest.transaction do
-      MergeRequest.where(closed: true, merged: true).update_all("state = 'merged'")
-      MergeRequest.where(closed: true, merged: true).update_all("state = 'closed'")
-      MergeRequest.where(closed: false).update_all("state = 'opened'")
+      MergeRequest.where(closed: true, merged: true).update_all(state: :merged)
+      MergeRequest.where(closed: true, merged: false).update_all(state: :closed)
+      MergeRequest.where(closed: false).update_all(state: :opened)
     end
   end
 
index 7809666..1978ea8 100644 (file)
@@ -1,14 +1,14 @@
 class ConvertClosedToStateInMilestone < ActiveRecord::Migration
   def up
     Milestone.transaction do
-      Milestone.where(closed: false).update_all("state = 'opened'")
-      Milestone.where(closed: false).update_all("state = 'active'")
+      Milestone.where(closed: true).update_all(state: :closed)
+      Milestone.where(closed: false).update_all(state: :active)
     end
   end
 
   def down
     Milestone.transaction do
-      Milestone.where(state: :closed).update_all("closed = 1")
+      Milestone.where(state: :closed).update_all(closed: true)
     end
   end
 end
diff --git a/db/migrate/20130304104623_add_state_to_user.rb b/db/migrate/20130304104623_add_state_to_user.rb
new file mode 100644 (file)
index 0000000..8154c21
--- /dev/null
@@ -0,0 +1,5 @@
+class AddStateToUser < ActiveRecord::Migration
+  def change
+    add_column :users, :state, :string
+  end
+end
diff --git a/db/migrate/20130304104740_convert_blocked_to_state.rb b/db/migrate/20130304104740_convert_blocked_to_state.rb
new file mode 100644 (file)
index 0000000..e8d5257
--- /dev/null
@@ -0,0 +1,14 @@
+class ConvertBlockedToState < ActiveRecord::Migration
+  def up
+    User.transaction do
+      User.where(blocked: true).update_all(state: :blocked)
+      User.where(blocked: false).update_all(state: :active)
+    end
+  end
+
+  def down
+    User.transaction do
+      User.where(state: :blocked).update_all(blocked: :true)
+    end
+  end
+end
diff --git a/db/migrate/20130304105317_remove_blocked_from_user.rb b/db/migrate/20130304105317_remove_blocked_from_user.rb
new file mode 100644 (file)
index 0000000..e010474
--- /dev/null
@@ -0,0 +1,9 @@
+class RemoveBlockedFromUser < ActiveRecord::Migration
+  def up
+    remove_column :users, :blocked
+  end
+
+  def down
+    add_column :users, :blocked, :boolean
+  end
+end
index 74d5f9a..2250f41 100644 (file)
@@ -11,7 +11,7 @@
 #
 # It's strongly recommended to check this file into your version control system.
 
-ActiveRecord::Schema.define(:version => 20130220133245) do
+ActiveRecord::Schema.define(:version => 20130304105317) do
 
   create_table "events", :force => true do |t|
     t.string   "target_type"
@@ -112,6 +112,7 @@ ActiveRecord::Schema.define(:version => 20130220133245) do
     t.datetime "created_at",                  :null => false
     t.datetime "updated_at",                  :null => false
     t.string   "type"
+    t.string   "description", :default => "", :null => false
   end
 
   add_index "namespaces", ["name"], :name => "index_namespaces_on_name"
@@ -152,6 +153,8 @@ ActiveRecord::Schema.define(:version => 20130220133245) do
     t.boolean  "wiki_enabled",           :default => true,     :null => false
     t.integer  "namespace_id"
     t.boolean  "public",                 :default => false,    :null => false
+    t.string   "issues_tracker",         :default => "gitlab", :null => false
+    t.string   "issues_tracker_id"
   end
 
   add_index "projects", ["creator_id"], :name => "index_projects_on_owner_id"
@@ -232,6 +235,7 @@ ActiveRecord::Schema.define(:version => 20130220133245) do
     t.integer  "owner_id"
     t.datetime "created_at",                  :null => false
     t.datetime "updated_at",                  :null => false
+    t.string   "description", :default => "", :null => false
   end
 
   create_table "users", :force => true do |t|
@@ -257,7 +261,6 @@ ActiveRecord::Schema.define(:version => 20130220133245) do
     t.boolean  "dark_scheme",            :default => false, :null => false
     t.integer  "theme_id",               :default => 1,     :null => false
     t.string   "bio"
-    t.boolean  "blocked",                :default => false, :null => false
     t.integer  "failed_attempts",        :default => 0
     t.datetime "locked_at"
     t.string   "extern_uid"
@@ -265,10 +268,10 @@ ActiveRecord::Schema.define(:version => 20130220133245) do
     t.string   "username"
     t.boolean  "can_create_group",       :default => true,  :null => false
     t.boolean  "can_create_team",        :default => true,  :null => false
+    t.string   "state"
   end
 
   add_index "users", ["admin"], :name => "index_users_on_admin"
-  add_index "users", ["blocked"], :name => "index_users_on_blocked"
   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"
index 6188260..2c4fb9d 100644 (file)
@@ -12,7 +12,7 @@ GitLab supports the following databases:
     sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev
 
     # Login to MySQL
-    mysql -u root -p
+    mysql -u root -p
 
     # Create a user for GitLab. (change $password to a real password)
     mysql> CREATE USER 'gitlab'@'localhost' IDENTIFIED BY '$password';
index 4d2ab63..d0f586a 100644 (file)
@@ -1,7 +1,6 @@
-This installation guide was created for Debian/Ubuntu and tested on it.
-
-Please read [`doc/install/requirements.md`](./requirements.md) for hardware and platform requirements.
+This installation guide was created for Debian/Ubuntu and tested on it. Please read [`doc/install/requirements.md`](./requirements.md) for hardware and platform requirements.
 
+This installation guide is recommended to set up a production server. If you want a development environment please use the [Vargrant virtual machine](https://github.com/gitlabhq/gitlab-vagrant-vm) since it makes it much easier to set up all the dependencies for integration testing.
 
 **Important Note:**
 The following steps have been known to work.
@@ -92,21 +91,29 @@ Create a `git` user for Gitlab:
 
     sudo adduser --disabled-login --gecos 'GitLab' git
 
+
 # 4. GitLab shell
 
-    # Login as git 
+GitLab Shell is a ssh access and repository management software developed specially for GitLab.
+
+    # Login as git
     sudo su git
 
-    # Go to home directory 
+    # Go to home directory
     cd /home/git
 
     # Clone gitlab shell
     git clone https://github.com/gitlabhq/gitlab-shell.git
 
-    # Setup
     cd gitlab-shell
     cp config.yml.example config.yml
-    ./bin/install 
+
+    # Edit config and replace gitlab_url
+    # with something like 'http://domain.com/'
+    vim config.yml
+
+    # Do setup
+    ./bin/install
 
 
 # 5. Database
@@ -124,9 +131,9 @@ To setup the MySQL/PostgreSQL database and dependencies please see [`doc/install
     # Clone GitLab repository
     sudo -u git -H git clone https://github.com/gitlabhq/gitlabhq.git gitlab
 
-    # Go to gitlab dir 
+    # Go to gitlab dir
     cd /home/git/gitlab
-   
+
     # Checkout to stable release
     sudo -u git -H git checkout 5-0-stable
 
@@ -157,7 +164,7 @@ do so with caution!
     # Create directory for pids and make sure GitLab can write to it
     sudo -u git -H mkdir tmp/pids/
     sudo chmod -R u+rwX  tmp/pids/
+
     # Copy the example Unicorn config
     sudo -u git -H cp config/unicorn.rb.example config/unicorn.rb
 
@@ -188,7 +195,7 @@ Make sure to update username/password in config/database.yml.
 
 
 ## Initialise Database and Activate Advanced Features
-    
+
     sudo -u git -H bundle exec rake db:setup RAILS_ENV=production
     sudo -u git -H bundle exec rake db:seed_fu RAILS_ENV=production
     sudo -u git -H bundle exec rake gitlab:setup RAILS_ENV=production
@@ -286,7 +293,7 @@ a different host, you can configure its connection string via the
 ## Custom SSH Connection
 
 If you are running SSH on a non-standard port, you must change the gitlab user's SSH config.
-    
+
     # Add to /home/git/.ssh/config
     host localhost          # Give your setup a name (here: override localhost)
         user git            # Your remote git user
index 31ce5ad..a6cbd2c 100644 (file)
@@ -7,3 +7,19 @@ Feature: Project Network Graph
   @javascript
   Scenario: I should see project network
     Then page should have network graph
+    And page should select "master" in select box
+    And page should have "master" on graph
+
+  @javascript
+  Scenario: I should switch ref to "stable"
+    When I switch ref to "stable"
+    Then page should have network graph
+    And page should select "stable" in select box
+    And page should have "stable" on graph
+
+  @javascript
+  Scenario: I should looking for a commit by SHA of "v2.1.0"
+    When I looking for a commit by SHA of "v2.1.0"
+    Then page should have network graph
+    And page should select "master" in select box
+    And page should have "v2.1.0" on graph
index cbca2da..167763b 100644 (file)
@@ -25,11 +25,13 @@ class AdminGroups < Spinach::FeatureSteps
 
   And 'submit form with new group info' do
     fill_in 'group_name', :with => 'gitlab'
+    fill_in 'group_description', :with => 'Group description'
     click_button "Create group"
   end
 
   Then 'I should see newly created group' do
     page.should have_content "Group: gitlab"
+    page.should have_content "Group description"
   end
 
   Then 'I should be redirected to group page' do
index 637fc4e..6423f3d 100644 (file)
@@ -18,6 +18,7 @@ class AdminTeams < Spinach::FeatureSteps
 
   And 'submit form with new team info' do
     fill_in 'user_team_name', with: 'gitlab'
+    fill_in 'user_team_description', with: 'description'
     click_button 'Create team'
   end
 
@@ -27,6 +28,7 @@ class AdminTeams < Spinach::FeatureSteps
 
   And 'I should see newly created team' do
     page.should have_content "Team: gitlab"
+    page.should have_content "description"
   end
 
   When 'I visit admin teams page' do
index 5cfa475..5ac958e 100644 (file)
@@ -28,7 +28,7 @@ class Groups < Spinach::FeatureSteps
 
   Then 'I should see merge requests from this group assigned to me' do
     assigned_to_me(:merge_requests).each do |issue|
-      page.should have_content issue.title
+      page.should have_content issue.title[0..80]
     end
   end
 
@@ -69,12 +69,14 @@ class Groups < Spinach::FeatureSteps
   end
 
   And 'submit form with new group info' do
-    fill_in 'group_name', :with => 'Samurai'
+    fill_in 'group_name', with: 'Samurai'
+    fill_in 'group_description', with: 'Tokugawa Shogunate'
     click_button "Create group"
   end
 
   Then 'I should see newly created group' do
     page.should have_content "Samurai"
+    page.should have_content "Tokugawa Shogunate"
     page.should have_content "You will only see events from projects in this group"
   end
 
index ff95a47..4c22119 100644 (file)
@@ -25,8 +25,8 @@ class ProjectMergeRequests < Spinach::FeatureSteps
   end
 
   Then 'I should see closed merge request "Bug NS-04"' do
-    mr = MergeRequest.find_by_title("Bug NS-04")
-    mr.closed?.should be_true
+    merge_request = MergeRequest.find_by_title!("Bug NS-04")
+    merge_request.closed?.should be_true
     page.should have_content "Closed by"
   end
 
@@ -63,7 +63,6 @@ class ProjectMergeRequests < Spinach::FeatureSteps
   end
 
   And 'project "Shop" have "Bug NS-04" open merge request' do
-    project = Project.find_by_name("Shop")
     create(:merge_request,
            title: "Bug NS-04",
            project: project,
@@ -71,7 +70,6 @@ class ProjectMergeRequests < Spinach::FeatureSteps
   end
 
   And 'project "Shop" have "Bug NS-05" open merge request with diffs inside' do
-    project = Project.find_by_name("Shop")
     create(:merge_request_with_diffs,
            title: "Bug NS-05",
            project: project,
@@ -79,7 +77,6 @@ class ProjectMergeRequests < Spinach::FeatureSteps
   end
 
   And 'project "Shop" have "Feature NS-03" closed merge request' do
-    project = Project.find_by_name("Shop")
     create(:closed_merge_request,
            title: "Feature NS-03",
            project: project,
@@ -87,18 +84,16 @@ class ProjectMergeRequests < Spinach::FeatureSteps
   end
 
   And 'I switch to the diff tab' do
-    mr = MergeRequest.find_by_title("Bug NS-05")
-    visit diffs_project_merge_request_path(mr.project, mr)
+    visit diffs_project_merge_request_path(project, merge_request)
   end
 
   And 'I switch to the merge request\'s comments tab' do
-    mr = MergeRequest.find_by_title("Bug NS-05")
-    visit project_merge_request_path(mr.project, mr)
+    visit project_merge_request_path(project, merge_request)
   end
 
   And 'I click on the first commit in the merge request' do
-    mr = MergeRequest.find_by_title("Bug NS-05")
-    click_link mr.commits.first.short_id(8)
+
+    click_link merge_request.commits.first.short_id(8)
   end
 
   And 'I leave a comment on the diff page' do
@@ -121,8 +116,7 @@ class ProjectMergeRequests < Spinach::FeatureSteps
   end
 
   Then 'I should see a discussion has started on line 185' do
-    mr = MergeRequest.find_by_title("Bug NS-05")
-    first_commit = mr.commits.first
+    first_commit = merge_request.commits.first
     first_diff   = first_commit.diffs.first
     page.should have_content "#{current_user.name} started a discussion on this merge request diff"
     page.should have_content "#{first_diff.b_path}:L185"
@@ -130,8 +124,7 @@ class ProjectMergeRequests < Spinach::FeatureSteps
   end
 
   Then 'I should see a discussion has started on commit bcf03b5de6c:L185' do
-    mr = MergeRequest.find_by_title("Bug NS-05")
-    first_commit = mr.commits.first
+    first_commit = merge_request.commits.first
     first_diff   = first_commit.diffs.first
     page.should have_content "#{current_user.name} started a discussion on commit"
     page.should have_content first_commit.short_id(8)
@@ -140,12 +133,19 @@ class ProjectMergeRequests < Spinach::FeatureSteps
   end
 
   Then 'I should see a discussion has started on commit bcf03b5de6c' do
-    mr = MergeRequest.find_by_title("Bug NS-05")
-    first_commit = mr.st_commits.first
+    first_commit = merge_request.st_commits.first
     first_diff   = first_commit.diffs.first
     page.should have_content "#{current_user.name} started a discussion on commit bcf03b5de6c"
     page.should have_content first_commit.short_id(8)
     page.should have_content "One comment to rule them all"
     page.should have_content "#{first_diff.b_path}:L185"
   end
+
+  def project
+    @project ||= Project.find_by_name!("Shop")
+  end
+
+  def merge_request
+    @merge_request ||= MergeRequest.find_by_title!("Bug NS-05")
+  end
 end
index f26deff..2ca6298 100644 (file)
@@ -4,16 +4,51 @@ class ProjectNetworkGraph < Spinach::FeatureSteps
 
   Then 'page should have network graph' do
     page.should have_content "Project Network Graph"
-    within ".graph" do
-      page.should have_content "master"
-    end
+    page.should have_selector ".graph"
   end
 
-  And 'I visit project "Shop" network page' do
+  When 'I visit project "Shop" network page' do
     # Stub Graph::JsonBuilder max_size to speed up test (10 commits vs. 650)
-    Gitlab::Graph::JsonBuilder.stub(max_count: 10)
+    Graph::JsonBuilder.stub(max_count: 10)
 
     project = Project.find_by_name("Shop")
     visit project_graph_path(project, "master")
   end
+
+  And 'page should select "master" in select box' do
+    page.should have_selector '#ref_chzn span', :text => "master"
+  end
+
+  And 'page should have "master" on graph' do
+    within '.graph' do
+      page.should have_content 'master'
+    end
+  end
+
+  And 'I switch ref to "stable"' do
+    page.select 'stable', :from => 'ref'
+  end
+
+  And 'page should select "stable" in select box' do
+    page.should have_selector '#ref_chzn span', :text => "stable"
+  end
+
+  And 'page should have "stable" on graph' do
+    within '.graph' do
+      page.should have_content 'stable'
+    end
+  end
+
+  And 'I looking for a commit by SHA of "v2.1.0"' do
+    within ".content .search" do
+      fill_in 'q', :with => '98d6492'
+      find('button').click
+    end
+  end
+
+  And 'page should have "v2.1.0" on graph' do
+    within '.graph' do
+      page.should have_content 'v2.1.0'
+    end
+  end
 end
index 40786f6..431d529 100644 (file)
@@ -143,7 +143,7 @@ module SharedPaths
 
   Given "I visit my project's network page" do
     # Stub Graph::JsonBuilder max_size to speed up test (10 commits vs. 650)
-    Gitlab::Graph::JsonBuilder.stub(max_count: 10)
+    Graph::JsonBuilder.stub(max_count: 10)
 
     visit project_graph_path(@project, root_ref)
   end
index 1abb0f4..862259d 100644 (file)
@@ -44,9 +44,16 @@ class Userteams < Spinach::FeatureSteps
 
     And 'I submit form with new team info' do
       fill_in 'name', with: 'gitlab'
+
+      fill_in 'user_team_description', with: 'team description'
       click_button 'Create team'
     end
 
+    And 'I should see newly created team' do
+      page.should have_content "gitlab"
+      page.should have_content "team description"
+    end
+
     Then 'I should be redirected to new team page' do
       team = UserTeam.last
       current_path.should == team_path(team)
index da40b38..2fd7ffd 100644 (file)
@@ -34,6 +34,7 @@ Spinach.hooks.before_scenario do
   Gitlab.config.gitlab_shell.stub(repos_path: Rails.root.join('tmp', 'test-git-base-path'))
   FileUtils.rm_rf Gitlab.config.gitlab_shell.repos_path
   FileUtils.mkdir_p Gitlab.config.gitlab_shell.repos_path
+  DatabaseCleaner.start
 end
 
 Spinach.hooks.after_scenario do
index 9255e0d..f777459 100644 (file)
@@ -20,6 +20,7 @@ Feature: UserTeams
     When I click to "New team" link
     And I submit form with new team info
     Then I should be redirected to new team page
+    Then I should see newly created team
 
   Scenario: I should see team dashboard list
     When I have teams with projects and members
index 1cae1d3..088c995 100644 (file)
@@ -2,11 +2,11 @@ module Gitlab
   module Entities
     class User < Grape::Entity
       expose :id, :username, :email, :name, :bio, :skype, :linkedin, :twitter,
-             :dark_scheme, :theme_id, :blocked, :created_at, :extern_uid, :provider
+             :dark_scheme, :theme_id, :state, :created_at, :extern_uid, :provider
     end
 
     class UserBasic < Grape::Entity
-      expose :id, :username, :email, :name, :blocked, :created_at
+      expose :id, :username, :email, :name, :state, :created_at
     end
 
     class UserLogin < UserBasic
index abd115d..6df00db 100644 (file)
@@ -52,8 +52,8 @@ module Gitlab
                                     :issues_enabled,
                                     :wall_enabled,
                                     :merge_requests_enabled,
-                                    :wiki_enabled]
-
+                                    :wiki_enabled,
+                                    :namespace_id]
         @project = ::Projects::CreateContext.new(current_user, attrs).execute
         if @project.saved?
           present @project, with: Entities::Project
index fb595e1..fd0050c 100644 (file)
@@ -126,7 +126,7 @@ module ExtractsPath
     @tree = TreeDecorator.new(@tree)
 
     raise InvalidPathError if @tree.invalid?
-  rescue NoMethodError, InvalidPathError
+  rescue RuntimeError, NoMethodError, InvalidPathError
     not_found!
   end
 end
index d0e792b..0fee33d 100644 (file)
@@ -41,10 +41,12 @@ module Gitlab
         password_confirmation: password,
         projects_limit: Gitlab.config.gitlab.default_projects_limit,
       }, as: :admin)
+      @user.save!
+
       if Gitlab.config.omniauth['block_auto_created_users'] && !ldap
-        @user.blocked = true
+        @user.block
       end
-      @user.save!
+
       @user
     end
 
diff --git a/lib/gitlab/graph/commit.rb b/lib/gitlab/graph/commit.rb
deleted file mode 100644 (file)
index 13c8ebc..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-require "grit"
-
-module Gitlab
-  module Graph
-    class Commit
-      include ActionView::Helpers::TagHelper
-
-      attr_accessor :time, :space, :refs, :parent_spaces
-
-      def initialize(commit)
-        @_commit = commit
-        @time = -1
-        @space = 0
-        @parent_spaces = []
-      end
-
-      def method_missing(m, *args, &block)
-        @_commit.send(m, *args, &block)
-      end
-
-      def to_graph_hash
-        h = {}
-        h[:parents] = self.parents.collect do |p|
-          [p.id,0,0]
-        end
-        h[:author]  = {
-          name: author.name, 
-          email: author.email
-        }
-        h[:time]    = time
-        h[:space]   = space
-        h[:parent_spaces]   = parent_spaces
-        h[:refs]    = refs.collect{|r|r.name}.join(" ") unless refs.nil?
-        h[:id]      = sha
-        h[:date]    = date
-        h[:message] = message
-        h
-      end
-
-      def add_refs(ref_cache, repo)
-        if ref_cache.empty?
-          repo.refs.each do |ref|
-            ref_cache[ref.commit.id] ||= []
-            ref_cache[ref.commit.id] << ref
-          end
-        end
-        @refs = ref_cache[@_commit.id] if ref_cache.include?(@_commit.id)
-        @refs ||= []
-      end
-    end
-  end
-end
diff --git a/lib/gitlab/graph/json_builder.rb b/lib/gitlab/graph/json_builder.rb
deleted file mode 100644 (file)
index cc971a2..0000000
+++ /dev/null
@@ -1,268 +0,0 @@
-require "grit"
-
-module Gitlab
-  module Graph
-    class JsonBuilder
-      attr_accessor :days, :commits, :ref_cache, :repo
-
-      def self.max_count
-        @max_count ||= 650
-      end
-
-      def initialize project, ref, commit
-        @project = project
-        @ref = ref
-        @commit = commit
-        @repo = project.repo
-        @ref_cache = {}
-
-        @commits = collect_commits
-        @days = index_commits
-      end
-
-      def to_json(*args)
-        {
-          days: @days.compact.map { |d| [d.day, d.strftime("%b")] },
-          commits: @commits.map(&:to_graph_hash)
-        }.to_json(*args)
-      end
-
-    protected
-
-      # Get commits from repository
-      #
-      def collect_commits
-
-        @commits = Grit::Commit.find_all(repo, nil, {topo_order: true, max_count: self.class.max_count, skip: to_commit}).dup
-
-        # Decorate with app/models/commit.rb
-        @commits.map! { |commit| ::Commit.new(commit) }
-
-        # Decorate with lib/gitlab/graph/commit.rb
-        @commits.map! { |commit| Gitlab::Graph::Commit.new(commit) }
-
-        # add refs to each commit
-        @commits.each { |commit| commit.add_refs(ref_cache, repo) }
-
-        @commits
-      end
-
-      # Method is adding time and space on the
-      # list of commits. As well as returns date list
-      # corelated with time set on commits.
-      #
-      # @param [Array<Graph::Commit>] commits to index
-      #
-      # @return [Array<TimeDate>] list of commit dates corelated with time on commits
-      def index_commits
-        days, times = [], []
-        map = {}
-
-        commits.reverse.each_with_index do |c,i|
-          c.time = i
-          days[i] = c.committed_date
-          map[c.id] = c
-          times[i] = c
-        end
-
-        @_reserved = {}
-        days.each_index do |i|
-          @_reserved[i] = []
-        end
-
-        commits_sort_by_ref.each do |commit|
-          if map.include? commit.id then
-            place_chain(map[commit.id], map)
-          end
-        end
-
-        # find parent spaces for not overlap lines
-        times.each do |c|
-          c.parent_spaces.concat(find_free_parent_spaces(c, map, times))
-        end
-
-        days
-      end
-
-      # Skip count that the target commit is displayed in center.
-      def to_commit
-        commits = Grit::Commit.find_all(repo, nil, {topo_order: true})
-        commit_index = commits.index do |c|
-          c.id == @commit.id
-        end
-
-        if commit_index && (self.class.max_count / 2 < commit_index) then
-          # get max index that commit is displayed in the center.
-          commit_index - self.class.max_count / 2
-        else
-          0
-        end
-      end
-
-      def commits_sort_by_ref
-        commits.sort do |a,b|
-          if include_ref?(a)
-            -1
-          elsif include_ref?(b)
-            1
-          else
-            b.committed_date <=> a.committed_date
-          end
-        end
-      end
-
-      def include_ref?(commit)
-        heads = commit.refs.select do |ref|
-          ref.is_a?(Grit::Head) or ref.is_a?(Grit::Remote) or ref.is_a?(Grit::Tag)
-        end
-
-        heads.map! do |head|
-          head.name
-        end
-
-        heads.include?(@ref)
-      end
-
-      def find_free_parent_spaces(commit, map, times)
-        spaces = []
-
-        commit.parents.each do |p|
-          if map.include?(p.id) then
-            parent = map[p.id]
-
-            range = if commit.time < parent.time then
-                      commit.time..parent.time
-                    else
-                      parent.time..commit.time
-                    end
-
-            space = if commit.space >= parent.space then
-                      find_free_parent_space(range, parent.space, 1, commit.space, times)
-                    else
-                      find_free_parent_space(range, parent.space, -1, parent.space, times)
-                    end
-
-            mark_reserved(range, space)
-            spaces << space
-          end
-        end
-
-        spaces
-      end
-
-      def find_free_parent_space(range, space_base, space_step, space_default, times)
-        if is_overlap?(range, times, space_default) then
-          find_free_space(range, space_base, space_step)
-        else
-          space_default
-        end
-      end
-
-      def is_overlap?(range, times, overlap_space)
-        range.each do |i|
-          if i != range.first &&
-            i != range.last &&
-            times[i].space == overlap_space then
-
-            return true;
-          end
-        end
-
-        false
-      end
-
-      # Add space mark on commit and its parents
-      #
-      # @param [Graph::Commit] the commit object.
-      # @param [Hash<String,Graph::Commit>] map of commits
-      def place_chain(commit, map, parent_time = nil)
-        leaves = take_left_leaves(commit, map)
-        if leaves.empty?
-          return
-        end
-        # and mark it as reserved
-        min_time = leaves.last.time
-        max_space = 1
-        parents = leaves.last.parents.collect
-        parents.each do |p|
-          if map.include? p.id
-            parent = map[p.id]
-            if parent.time < min_time
-              min_time = parent.time
-            end
-            if max_space < parent.space then
-              max_space = parent.space
-            end
-          end
-        end
-        if parent_time.nil?
-          max_time = leaves.first.time
-        else
-          max_time = parent_time - 1
-        end
-
-        time_range = leaves.last.time..leaves.first.time
-        space = find_free_space(time_range, max_space, 2)
-        leaves.each{|l| l.space = space}
-
-        mark_reserved(min_time..max_time, space)
-
-        # Visit branching chains
-        leaves.each do |l|
-          parents = l.parents.collect.select{|p| map.include? p.id and map[p.id].space.zero?}
-          for p in parents
-            place_chain(map[p.id], map, l.time)
-          end
-        end
-      end
-
-      def mark_reserved(time_range, space)
-        for day in time_range
-          @_reserved[day].push(space)
-        end
-      end
-
-      def find_free_space(time_range, space_base, space_step)
-        reserved = []
-        for day in time_range
-          reserved += @_reserved[day]
-        end
-        reserved.uniq!
-
-        space = space_base
-        while reserved.include?(space) do
-          space += space_step
-          if space <= 0 then
-            space_step *= -1
-            space = space_base + space_step
-          end
-        end
-
-        space
-      end
-
-      # Takes most left subtree branch of commits
-      # which don't have space mark yet.
-      #
-      # @param [Graph::Commit] the commit object.
-      # @param [Hash<String,Graph::Commit>] map of commits
-      #
-      # @return [Array<Graph::Commit>] list of branch commits
-      def take_left_leaves(commit, map)
-        leaves = []
-        leaves.push(commit) if commit.space.zero?
-
-        while true
-          return leaves if commit.parents.count.zero?
-          return leaves unless map.include? commit.parents.first.id
-
-          commit = map[commit.parents.first.id]
-
-          return leaves unless commit.space.zero?
-
-          leaves.push(commit)
-        end
-      end
-    end
-  end
-end
index e7d6e3e..280f9f9 100644 (file)
@@ -25,6 +25,8 @@ module Gitlab
   #   >> gfm(":trollface:")
   #   => "<img alt=\":trollface:\" class=\"emoji\" src=\"/images/trollface.png" title=\":trollface:\" />
   module Markdown
+    include IssuesHelper
+
     attr_reader :html_options
 
     # Public: Parse the provided text with GitLab-Flavored Markdown
@@ -163,8 +165,11 @@ module Gitlab
     end
 
     def reference_issue(identifier)
-      if issue = @project.issues.where(id: identifier).first
-        link_to("##{identifier}", project_issue_url(@project, issue), html_options.merge(title: "Issue: #{issue.title}", class: "gfm gfm-issue #{html_options[:class]}"))
+      if @project.issue_exists? identifier
+        url = url_for_issue(identifier)
+        title = title_for_issue(identifier)
+
+        link_to("##{identifier}", url, html_options.merge(title: "Issue: #{title}", class: "gfm gfm-issue #{html_options[:class]}"))
       end
     end
 
index cf99951..d0e9dfe 100644 (file)
@@ -1,19 +1,19 @@
 namespace :sidekiq do
   desc "GITLAB | Stop sidekiq"
   task :stop do
-    run "bundle exec sidekiqctl stop #{pidfile}"
+    system "bundle exec sidekiqctl stop #{pidfile}"
   end
 
   desc "GITLAB | Start sidekiq"
   task :start do
-    run "nohup bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,gitlab_shell,common,default -e #{Rails.env} -P #{pidfile} >> #{Rails.root.join("log", "sidekiq.log")} 2>&1 &"
+    system "nohup bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,gitlab_shell,common,default -e #{Rails.env} -P #{pidfile} >> #{Rails.root.join("log", "sidekiq.log")} 2>&1 &"
   end
-  
+
   desc "GITLAB | Start sidekiq with launchd on Mac OS X"
   task :launchd do
-    run "bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,gitlab_shell,common,default -e #{Rails.env} -P #{pidfile} >> #{Rails.root.join("log", "sidekiq.log")} 2>&1"
+    system "bundle exec sidekiq -q post_receive,mailer,system_hook,project_web_hook,gitlab_shell,common,default -e #{Rails.env} -P #{pidfile} >> #{Rails.root.join("log", "sidekiq.log")} 2>&1"
   end
-  
+
   def pidfile
     Rails.root.join("tmp", "pids", "sidekiq.pid")
   end
index b81984b..4176685 100644 (file)
@@ -29,6 +29,11 @@ FactoryGirl.define do
     creator
   end
 
+  factory :redmine_project, parent: :project do
+    issues_tracker { "redmine" }
+    issues_tracker_id { "project_name_in_redmine" }
+  end
+
   factory :group do
     sequence(:name) { |n| "group#{n}" }
     path { name.downcase.gsub(/\s/, '_') }
index 1a9ae8e..8d1ee11 100644 (file)
@@ -15,6 +15,7 @@
 FactoryGirl.define do
   factory :user_team do
     sequence(:name) { |n| "team#{n}" }
+    sequence(:description) { |n| "team_description#{n}" }
     path { name.downcase.gsub(/\s/, '_') }
     owner
   end
index 77d6e9e..22d1ee9 100644 (file)
@@ -55,8 +55,8 @@ describe "Admin::Users" do
         user = User.last
         email = ActionMailer::Base.deliveries.last
         email.subject.should have_content("Account was created")
-        email.body.should have_content(user.email)
-        email.body.should have_content(@password)
+        email.text_part.body.should have_content(user.email)
+        email.text_part.body.should have_content(@password)
       end
     end
 
@@ -67,8 +67,8 @@ describe "Admin::Users" do
         user = User.last
         email = ActionMailer::Base.deliveries.last
         email.subject.should have_content("Account was created")
-        email.body.should have_content(user.email)
-        email.body.should_not have_content(@password)
+        email.text_part.body.should have_content(user.email)
+        email.text_part.body.should_not have_content(@password)
       end
     end
   end
index 1b06797..1f5fabf 100644 (file)
@@ -2,6 +2,7 @@ require "spec_helper"
 
 describe GitlabMarkdownHelper do
   include ApplicationHelper
+  include IssuesHelper
 
   let!(:project) { create(:project) }
 
diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb
new file mode 100644 (file)
index 0000000..c9eb659
--- /dev/null
@@ -0,0 +1,79 @@
+require "spec_helper"
+
+describe IssuesHelper do
+  let(:project) { create :project }
+  let(:issue) { create :issue, project: project }
+  let(:ext_project) { create :redmine_project }
+
+  describe :title_for_issue do
+    it "should return issue title if used internal tracker" do
+      @project = project
+      title_for_issue(issue.id).should eq issue.title
+    end
+
+    it "should always return empty string if used external tracker" do
+      @project = ext_project
+      title_for_issue(rand(100)).should eq ""
+    end
+
+    it "should always return empty string if project nil" do
+      @project = nil
+
+      title_for_issue(rand(100)).should eq ""
+    end
+  end
+
+  describe :url_for_project_issues do
+    let(:project_url) { Gitlab.config.issues_tracker.redmine.project_url}
+    let(:ext_expected) do
+      project_url.gsub(':project_id', ext_project.id.to_s)
+                 .gsub(':issues_tracker_id', ext_project.issues_tracker_id.to_s)
+    end
+    let(:int_expected) { polymorphic_path([project]) }
+
+    it "should return internal path if used internal tracker" do
+      @project = project
+      url_for_project_issues.should match(int_expected)
+    end
+
+    it "should return path to external tracker" do
+      @project = ext_project
+
+      url_for_project_issues.should match(ext_expected)
+    end
+
+    it "should return empty string if project nil" do
+      @project = nil
+
+      url_for_project_issues.should eq ""
+    end
+  end
+
+  describe :url_for_issue do
+    let(:issue_id) { 3 }
+    let(:issues_url) { Gitlab.config.issues_tracker.redmine.issues_url}
+    let(:ext_expected) do
+      issues_url.gsub(':id', issue_id.to_s)
+        .gsub(':project_id', ext_project.id.to_s)
+        .gsub(':issues_tracker_id', ext_project.issues_tracker_id.to_s)
+    end
+    let(:int_expected) { polymorphic_path([project, issue]) }
+
+    it "should return internal path if used internal tracker" do
+      @project = project
+      url_for_issue(issue.id).should match(int_expected)
+    end
+
+    it "should return path to external tracker" do
+      @project = ext_project
+
+      url_for_issue(issue_id).should match(ext_expected)
+    end
+
+    it "should return empty string if project nil" do
+      @project = nil
+
+      url_for_issue(issue.id).should eq ""
+    end
+  end
+end
index 23f1c6d..545908b 100644 (file)
@@ -60,6 +60,7 @@ describe Project do
     it { should ensure_inclusion_of(:wall_enabled).in_array([true, false]) }
     it { should ensure_inclusion_of(:merge_requests_enabled).in_array([true, false]) }
     it { should ensure_inclusion_of(:wiki_enabled).in_array([true, false]) }
+    it { should ensure_length_of(:issues_tracker_id).is_within(0..255) }
 
     it "should not allow new projects beyond user limits" do
       project.stub(:creator).and_return(double(can_create_project?: false, projects_limit: 1))
@@ -190,4 +191,57 @@ describe Project do
       Project.new(path: "empty").repository.should be_nil
     end
   end
+
+  describe :issue_exists? do
+    let(:project) { create(:project) }
+    let(:existed_issue) { create(:issue, project: project) }
+    let(:not_existed_issue) { create(:issue) }
+    let(:ext_project) { create(:redmine_project) }
+
+    it "should be true or if used internal tracker and issue exists" do
+      project.issue_exists?(existed_issue.id).should be_true
+    end
+
+    it "should be false or if used internal tracker and issue not exists" do
+      project.issue_exists?(not_existed_issue.id).should be_false
+    end
+
+    it "should always be true if used other tracker" do
+      ext_project.issue_exists?(rand(100)).should be_true
+    end
+  end
+
+  describe :used_default_issues_tracker? do
+    let(:project) { create(:project) }
+    let(:ext_project) { create(:redmine_project) }
+
+    it "should be true if used internal tracker" do
+      project.used_default_issues_tracker?.should be_true
+    end
+
+    it "should be false if used other tracker" do
+      ext_project.used_default_issues_tracker?.should be_false
+    end
+  end
+
+  describe :can_have_issues_tracker_id? do
+    let(:project) { create(:project) }
+    let(:ext_project) { create(:redmine_project) }
+
+    it "should be true for projects with external issues tracker if issues enabled" do
+      ext_project.can_have_issues_tracker_id?.should be_true
+    end 
+
+    it "should be false for projects with internal issue tracker if issues enabled" do
+      project.can_have_issues_tracker_id?.should be_false
+    end
+
+    it "should be always false if issues disbled" do
+      project.issues_enabled = false
+      ext_project.issues_enabled = false
+
+      project.can_have_issues_tracker_id?.should be_false
+      ext_project.can_have_issues_tracker_id?.should be_false
+    end
+  end
 end
index 40047b3..cb39b6f 100644 (file)
@@ -25,7 +25,7 @@
 #  dark_scheme            :boolean          default(FALSE), not null
 #  theme_id               :integer          default(1), not null
 #  bio                    :string(255)
-#  blocked                :boolean          default(FALSE), not null
+#  state                  :string(255)      default(FALSE), not null
 #  failed_attempts        :integer          default(0)
 #  locked_at              :datetime
 #  extern_uid             :string(255)
@@ -140,7 +140,7 @@ describe User do
 
     it "should block user" do
       user.block
-      user.blocked.should be_true
+      user.blocked?.should be_true
     end
   end
 
@@ -149,7 +149,7 @@ describe User do
       User.delete_all
       @user = create :user
       @admin = create :user, admin: true
-      @blocked = create :user, blocked: true
+      @blocked = create :user, state: :blocked
     end
 
     it { User.filter("admins").should == [@admin] }
index bffa5fc..b58c564 100644 (file)
@@ -15,7 +15,13 @@ describe UserObserver do
       create(:user)
     end
 
+    it 'no email for external' do
+      Notify.should_not_receive(:new_user_email)
+      create(:user, extern_uid: '32442eEfsafada')
+    end
+
     it 'trigger logger' do
+      user = double(:user, id: 42, password: 'P@ssword!', name: 'John', email: 'u@mail.local', extern_uid?: false)
       Gitlab::AppLogger.should_receive(:info)
       create(:user)
     end